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\n",dfs(1,1)%mod);
    return 0;
}
posted @ 2019-11-25 17:59  贰冬  阅读(227)  评论(0编辑  收藏  举报