贪心1:首先可以规定,k(待接收数)中的所有0和1均按原顺序接收,即每个数不会比k中它前面的同类数早被接收,因为每一个这样的情况,都可以调整成按原顺序接收,所以这种情况得到的数删去对答案没有影响
例如上图,调整两个0的接收顺序,会发现两个数的最大时间延迟会变小,更容易符合条件
贪心2:每当需要接收一个0或1时,一定接收k中还未被接收的最靠左的,因为这个数早晚都要接收,留到后面接收容易超出时间限制。
所以dp就是dp[i][j]表示从k中从左到右接收i个0和j个1,能形成几种数。每次有接收0和1两种决策。但这样一定会产生许多不合法决策。判断方法就是,如果当前要接收一个0,最左边未被接收的1加上d小于这个0的位置,那么这个1以后就不可能接收到了,这样这个决策就不合法。如果等于也可以,可以在接收0的同时接收到这个1,把1排到同时接收的0的后面。
还有一个细节就是要用ull,ll会爆
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL unsigned long long
using namespace std;
int n,d,m1,m0,A[200],T[200][200];
LL k,dp[200][200];
LL get_max(){
int i,j;
LL k=0;
memset(T,0,sizeof(T));
for (i=1;i<=n;i++)
if (A[i]) T[i][1]++;
else T[i+d][0]++;
for (i=1;i<=n+d;i++){
for (j=1;j<=T[i][1];j++) k<<=1,k++;
for (j=1;j<=T[i][0];j++) k<<=1;
}
return k;
}
LL get_min(){
int i,j;
LL k=0;
memset(T,0,sizeof(T));
for (i=1;i<=n;i++)
if (!A[i]) T[i][0]++;
else T[i+d][1]++;
for (i=1;i<=n+d;i++){
for (j=1;j<=T[i][0];j++) k<<=1;
for (j=1;j<=T[i][1];j++) k<<=1,k++;
}
return k;
}
LL DP(){
memset(dp,0,sizeof(dp));
memset(T,0,sizeof(T));
m1=0; m0=0;
int i,j;
for (i=1;i<=n;i++)
if (A[i]) T[1][++m1]=i;
else T[0][++m0]=i;
dp[0][0]=1;
for (i=0;i<=m0;i++)
for (j=0;j<=m1;j++){
if (i<m0&&(j==m1||T[1][j+1]+d>=T[0][i+1])) dp[i+1][j]+=dp[i][j];
if (j<m1&&(i==m0||T[0][i+1]+d>=T[1][j+1])) dp[i][j+1]+=dp[i][j];
}
return dp[m0][m1];
}
int main(){
int cnt=0,m;
while (1){
scanf("%d",&n);
if (!n) break;
cin>>d>>k; m=n;
memset(A,0,sizeof(A));
while (k) A[m--]=k&1,k>>=1;
printf("Case %d: ",++cnt);
cout<<DP()<<' '<<get_min()<<' '<<get_max()<<endl;
}
}