zoukankan      html  css  js  c++  java
  • NOIP模拟 篮球比赛2

    题目描述

    由于Czhou举行了众多NOIP模拟赛,也导致放学后篮球比赛次数急剧增加。神牛们身体素质突飞猛进,并且球技不断精进。这引起了体育老师彩哥的注意,为了给校篮球队找到势均力敌的对手,彩哥找到了Czhou神,想要和机房篮球队进行多场友谊赛。Czhou为了顾全校篮球队面子,决定派出配合默契又不至于吊打校篮球队的阵容。
    而机房神牛的能力值受到游戏时长,训练时长,个人基础值得影响,可能会不断变化。所以Czhou想根据神牛当天状态从中选出若干名选手,使他们的能力值和等于k。
    Input:

    一行三个数n,k,l。表示机房有n个神牛,选出队员能力值和为k,每个神牛的能力最大值<=L且>=0。
    

    Ouput:

    输出一个数,表示方案数,方案满足选出若干选手使能力和为k。因为答案比较大,最后模10^9+7。
    

    Sample.in

    2 2 2
    

    Sample.out

    6
    

    样例说明:神牛的能力值可能为(0,2)(1,2)(1,1)(2,0)(2,1)(2,2),这样都可以选出一些数字使他们的能力值和为2。
    对于(0,2)表示第一只牛能力值为0,第二只牛能力为2
    类似的
    对于(1,2)选出2即满足要求。
    对于(1,1)选出全部选手即满足要求。
    所以(0,2)(1,1)都是满足要求的方案。
    数据范围:
    n,k<=20
    0<=L<=10^9.

    解法

    1.爆搜+背包

    这个当然是过不了的。

    2.状压DP(from hzwer)

    排列组合以及其余的dp算法似乎都会涉及重复统计的问题
    dp[i][state] 表示前i位,state的二进制每一位表示和为(1~k),1表示可以取到,0表示取不到,0<=state<(1<<K)-1
    枚举第i+1个取的数 f[i][j]->f[i+1][(i|(i<<x)|(1<<(x-1)))&((1<<K)-1)]
    这是一个2n*n2的dp,但是状压废状态比较多,剪掉废状态即可AC。。。

    3.记搜

    很明显,这道题是可以记搜的,代码注释很详细,也没什么要讲的。

    #include<bits/stdc++.h>
    const int mod=1e9+7;
    const int N=1<<21;
    typedef long long ll;
    using namespace std;
    int n,k,l,last,maxx;
    int Pow[27],f[24][1300007];
    /*
    	Pow记(l+1)的i次幂,,f是记搜.
    	f[i][j]的j是二进制表示的状态.0为取,1不取.
    	f[i][j]记当计算到第i个人状态为j时能够取到的值的的方案数。
    */
    int dfs(int cnt,int now){//cnt表示第几个人,now表示当前能得到的值的状态
        if(f[cnt][now]!=-1) return f[cnt][now];
        if(now&last) return f[cnt][now]=Pow[n-cnt+1];
        /*
    		如果此时已经可以取到k,则后面的人无论取多少都可以
          	后面的人取0~l的方案数为(l+1)的后面的人数(n-cnt+1)次方
         */
        if(cnt==n+1) return f[cnt][now]=(now&last);
        int res=0;
        long long temp;
        for(int i=0;i<=min(k,l);i++){
            temp=((ll)dfs(cnt+1,(now|(now<<i))&maxx))%mod;
            /*
            	&maxx防止超出n的范围
    		*/
            res=(res+temp)%mod;
        }
        if(l>k){
            temp=(((ll)((ll)dfs(cnt+1,now)%mod*(ll)(l-k))%mod))%mod;
            /*
    			如果当前的人取值大于k就一定不会做出贡献,对后面的状态的影响是等价的
                所以假设当前人取值大于k,跑一次.
            	所以直接计算一次再乘以(l-k)
    		*/
            res=(res+temp)%mod;
        }
        return f[cnt][now]=res%mod;
    }
    int main(){
    //	freopen("football.in","r",stdin);
    //	freopen("football.out","w",stdout);
        scanf("%d%d%d",&n,&k,&l);
        memset(f,-1,sizeof(f));
        Pow[0]=1;
        for(int i=1;i<=n;i++) Pow[i]=(ll)((ll)Pow[i-1]*(ll)(l+1))%mod;
        last=(1<<k);
        maxx=(1<<(k+1))-1;
        printf("%d
    ",dfs(1,1)%mod);
        return 0;
    }
    
  • 相关阅读:
    Step By Step(Lua-C API简介)
    Step By Step(Lua系统库)
    复制控制( 下 ) --- 自定义析构函数
    复制控制( 中 ) --- 重载赋值运算符
    复制控制( 上 ) --- 自定义复制函数
    泛型算法结构
    流迭代器 + 算法灵活控制IO流
    一个文本查询程序的实现
    multimap容器和multiset容器中的find操作
    实用的关联容器
  • 原文地址:https://www.cnblogs.com/Fast-Bird/p/11929350.html
Copyright © 2011-2022 走看看