分析:求出最大值和最小值比较简单,使用贪心法,求最小值的时候我们让所有的0尽可能的向后延迟就可以了,求最大值则相反。 关键在于求出可以组合出的数字个数。
这就是组合数学版的dp了,我们让dp[i][j]表示当前i个0,和前j个1被接收后所能形成的数字个数,初始条件为dp[0][0] = 1; 决策有两种,第一种转移到dp[i+1][j]也就是多接收一个0,这时候定义F1[]数组记录1的发送时间,F0[]数组记录0的发送时间,那么如上方程的转移条件为 F1[j+1]+d >= F0[i+1],也就是第i+1个0能先于第j+1个1被接收。另一种转移到dp[i][j+1],也是一样的道理。最后注意一下边界的特判,dp[sum0][sum1]就是答案。
注意: 关于上述方法有人可能会问,我们在同一时刻接收到了多个数字,按照题目要求应该自由排列,为什么上述方法,完全没有体现到这一点呢? 不要被迷惑了,我们关心的是多少个1和0被接收,而不是哪个1先被接收,我们的循环里完全记录下了各种情况的个数。
坑点:这个题的数组范围很大,如果不用unsighed long long,就会爆long long,我也因为这个WA了很多次。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; #define LL unsigned long long #define N 70 int n,d,bit[N]; struct ID{ int s1,s0; }id[N]; LL k; void Get_Bit(){ LL tmp = k; int ip = 1; memset(bit,0,sizeof(bit)); while(tmp) { if(tmp&1) bit[ip] = 1; ip++; tmp >>= 1; } } LL mypow(int x){ LL tmp = 1; for(int i = 1;i <= x;i++) tmp *= 2; return tmp; } LL Get_Min(){ for(int i = 1;i <= n;i++){ if(bit[i]) {id[i].s1 = 1; id[i].s0 = 0;} else {id[i].s0 = 1; id[i].s1 = 0;} } for(int i = n;i >= 1;i--){ if(bit[i]){ id[i].s1--; if(i-d >= 1) id[i-d].s1++; else id[1].s1++; } } LL Min = 0; int ip = 0; for(int i = 1;i <= n;i++){ while(id[i].s1 > 0) {Min += mypow(ip); id[i].s1--; ip++;} while(id[i].s0 > 0) {ip++; id[i].s0--;} } // cout<<"MIN = "<<Min<<endl; return Min; } LL Get_Max(){ for(int i = 1;i <= n;i++){ if(bit[i]) {id[i].s1 = 1; id[i].s0 = 0;} else {id[i].s0 = 1; id[i].s1 = 0;} } for(int i = n;i >= 1;i--){ if(bit[i]==0){ id[i].s0--; if(i-d >= 1) id[i-d].s0++; else id[1].s0++; } } LL Max = 0; int ip = 0; for(int i = 1;i <= n;i++){ while(id[i].s0 > 0) {id[i].s0--; ip++;} while(id[i].s1 > 0) {Max += mypow(ip); ip++; id[i].s1--;} } // cout<<"MAX = "<<Max<<endl; return Max; } LL Get_Ans(){ LL dp[N][N]; int sum0=0,sum1=0,f1[N],f0[N]; memset(dp,0,sizeof(dp)); for(int i = n;i >= 1;i--){ if(bit[i]) f1[++sum1] = n-i+1; else f0[++sum0] = n-i+1; } dp[0][0] = 1; for(int i = 0;i <= sum0;i++){ for(int j = 0;j <= sum1;j++){ if(j == sum1 && i < sum0) dp[i+1][j] += dp[i][j]; if(i == sum0 && j < sum1) dp[i][j+1] += dp[i][j]; if(i==sum0 || j==sum1) continue; if(f1[j+1]+d >= f0[i+1]) dp[i+1][j] += dp[i][j]; if(f0[i+1]+d >= f1[j+1]) dp[i][j+1] += dp[i][j]; } } return dp[sum0][sum1]; } int main() { LL Min,Max,ans,ca=0; while(cin>>n){ if(n==0) break; cin>>d>>k; Get_Bit(); Min = Get_Min(); Max = Get_Max(); ans = Get_Ans(); cout<<"Case "<<++ca<<": "<<ans<<" "<<Min<<" "<<Max<<endl; } return 0; }