15145641

  分析:求出最大值和最小值比较简单,使用贪心法,求最小值的时候我们让所有的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;
}

 

posted on 2016-08-27 16:39  icode-xiaohu  阅读(234)  评论(0编辑  收藏  举报