zoukankan      html  css  js  c++  java
  • [COCI 2017-2018-2]


    san(1s64M)

    游戏世界中有N个楼从左到右排列,从左到右编号为1N,第i幢楼的高度为Hi,楼上的金币数为Gi,游戏可以从任意一个楼开始且包涵几步。每一步玩家可以从当前位置向右跳(可以跳过一些楼)但必须跳到不低于当前楼的高度的楼上。他到了楼上后,可以得到楼上的金币。他可以在跳任意步(可以是零步)后结束游戏,但是要保证收到的金币数要大于等于K,现在想知道共有多少不同的种方案满足游戏。两个方案不同是指至少有一个楼不一样的方案。

    输入:

    第一行两个数​N (1 ≤ ​N ≤ 40) and ​K (1 ≤ ​K ≤ 4 · 10​ 10​ )

    接下来N行,每行两个正整数,第i行用HiGi表示第i个楼的高度和上面的金币。 (1 ≤ Hi, ​Gi ≤ 109​ )

    输出一行一个数,表示方案总数。

    In​ ​test​ ​cases​ ​worth​ ​40%​ ​of​ ​total​ ​points,​ ​it​ ​will​ ​hold​ ​​N​ ​≤​ ​20.

    SAMPLE​​ ​​TESTS

    input input input

    4​ ​6

    2​ ​1

    6​ ​3

    7​ ​2

    5​ ​6

    Output

    3

    样例1对应的方案​ ​{1,​ ​2,​ ​3},​ ​{1,​ ​4}​ ​and​ ​{4}


    题目大意:
    给定N座楼,它们有各自的高度与收益,每次任意位置开始,可以跳到右边高度大于等于它的楼,到达可得收益,
    求收益>=K的方案数


    分析:
    方法一:
    N<=40可以搜索+剪枝,可能可以暴力过一过,考场上写的过了
    剪枝:
    预处理出mx 表示这个点往后跳可以获得的最大收益 搜索时可以很好预判
    预处理出way 表示这个点开始跳的方案数 当我们已经满足tot>=K 可以直接加上way 退出

    #include<bits/stdc++.h>
    using namespace std;
    
    long long mx[45];
    long long way[45];
    int n;long long K;
    
    struct Build
    {
        int h,v;
    }edge[45];
    
    long long ans;
    void dfs(int now,long long tot)
    {
        if(tot>=K) {ans+=way[now];return ;}
        if(now==n+1) return ;
        for(int i=now+1;i<=n;i++) 
        {
            if(edge[i].h>=edge[now].h)
            {
                if(tot+mx[i]<tot) continue;
                dfs(i,tot+edge[i].v);
            }
        }
    }
    
    int main()
    {
        freopen("san.in","r",stdin);
        freopen("san.out","w",stdout);
        scanf("%d%lld",&n,&K);
        for(int i=1;i<=n;i++) {scanf("%d%d",&edge[i].h,&edge[i].v);}
        mx[n]=edge[n].v;way[n]=1;
        for(int i=n-1;i>=1;i--)
        {
            for(int j=i+1;j<=n;j++)
            {
                if(edge[i].h<=edge[j].h) mx[i]=max(mx[i],mx[j]),way[i]+=way[j];
            }
            mx[i]+=edge[i].v;way[i]++;
        }
        
        for(int i=1;i<=n;i++)
        dfs(i,edge[i].v);
        
        printf("%lld
    ",ans);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code

    方法二:
    还是看N<=40,直接二进制枚举不完,发现可以拆成两部分
    mid=20 就可以直接枚举
    我们可以得到两部分分别的值,并把前一部分的结尾与w记录,把后一部分的开头与w记录
    因为分别的求了还要求跨区间的部分
    我们现在需要满足s1[i].w+s2[j].w>=K&&s1[i].last<=s2[i].first
    二维限制,先把w排序,pos从第一部分从右往左 我们发现是递减,那么是单调的,到达它所能最远达到的点


    这个点到mid的肯定都是满足的,都放入树状数组,然后找出<=first的数量

    #include<bits/stdc++.h>
    using namespace std;
    int n;long long K;
    
    struct data
    {
        int h,v;
    }edge[45];
    int all[45];
    long long ans;
    struct node
    {
        int h;long long w;
        bool operator < (const node & other)const 
        {
            return w<other.w;
        }
    }s1[10010],s2[10010];
    int len1,len2;
    
    int c[45];
    inline int lowbit(int x) {return x&(-x);}
    void add(int a,int b)
    {
        while(a<=n)
        {
            c[a]+=b;
            a+=lowbit(a);
        }
    }
    int sum(int a)
    {
        int s=0;
        while(a)
        {
            s+=c[a];
            a-=lowbit(a);
        }
        return s;
    }
    
    
    int main()
    {
        freopen("a.in","r",stdin);
    //    freopen("san.out","w",stdout);
        scanf("%d%lld",&n,&K);
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&edge[i].h,&edge[i].v);
            all[i]=edge[i].h;
        }
        sort(all,all+n);
        int mid=n>>1;
        int tot1=1<<mid,tot2=1<<(n-mid);
        for(int i=1;i<tot1;i++)
        {
            int pre=0;long long w=0;int flag=0;
            for(int j=0;j<mid;j++) 
            {
                if(i&(1<<j)) 
                {
                    if(edge[j].h>=pre) pre=edge[j].h,w+=edge[j].v;
                    else {flag=1;break;}
                }
            }
            if(flag) {continue;}
            if(w>=K) ans++;
            s1[len1++]=(node){pre,w};
        }
        for(int i=1;i<tot2;i++)
        {
            int first=0;int pre=0;long long w=0;int flag=0;
            for(int j=mid;j<n;j++) 
            {
                if(i&(1<<(j-mid)))
                {
                    if(!first) first=edge[j].h;
                    if(edge[j].h>=pre) pre=edge[j].h,w+=edge[j].v;
                    else {flag=1;break;}
                }
            }
            if(flag) {continue;}
            if(w>=K) ans++;
            s2[len2++]=(node){first,w};
        }
        sort(s1,s1+len1);sort(s2,s2+len2);
        
        int pos=len1-1;
        for(int i=0;i<len2;i++)
        {
            while(pos>=0&&s1[pos].w+s2[i].w>=K) 
            {
                int t=lower_bound(all,all+n,s1[pos].h)-all+1;
            add(t,1);pos--;
            }
            int t=lower_bound(all,all+n,s2[i].h)-all+1;
            ans+=sum(t);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    View Code

  • 相关阅读:
    如何在windows系统下安装swoole(Docker环境)
    如何在windows系统下安装swoole(cgywin环境)
    Docker的介绍及安装
    Java50道经典习题-程序48 数字加密
    Java50道经典习题-程序49 子串出现的个数
    Java50道经典习题-程序50 文件IO
    从键盘录入一个数据,输出对应的九九乘法表
    冒泡排序
    直接排序
    Math类的三个方法比较: floor() ceil() round()
  • 原文地址:https://www.cnblogs.com/Heey/p/8993855.html
Copyright © 2011-2022 走看看