贪心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; } }