UVA - 1228 Integer Transmission --动态规划 组合数学(递推)

问题

分析

这题看上去很复杂,但紫书上的思路很清晰,按照上面的思路写就比较简单了

首先是问题,这个问题是bit传输中会有[1-d+1]的延时,扰乱顺序,其中0,1数量不变,所以可以规定所有的0看作按照原顺序收到的,把所有的1看作按照原顺序收到的,这规定就把问题变为了两串数字0,1交替插值,0,1的序号之差要在d之内,所以我们可以用贪心法求解最大和最小值
假设原来1-n比特数字,传输后变为[1,n+d]这么长,但这个区间内实际有数字到达的时间不超过n,我们可以规定去除多余的延时,不影响结果,然后由于同一时间点到达的数字可以按照任意顺序排列,所以可以将其中的一些数字到达的时间合并,如紫书上的图9-25,总结出来的规律就是任意时刻k中已经被接收的位最右边那位一定没有延迟
下面是状态和状态转移,规定状态d(i,j)是前面i个0和j个1组合成的整数个数,下面就只有两种转移方式,一种是接着加0,转移到dp(i+1,j),另一种是接着加1,转移到dp(i,j+1),有点类似于归并排序是合并两个数组
添加时需要判断是否满足条件,就是两个数字间(i+1和j 或者 i和j+1 两种组合方式)的发射时间间隔小于d
总的时间复杂度 O ( n 2 ) O(n^2)

注意

要用unsigned long long存储状态

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <utility>
using namespace std;
const int maxn=70;
unsigned long long ans=0,n,d,k,maxv,minv,dp[maxn][maxn];  //long long会溢出
int kase=0,digit[maxn],digit0[maxn],digit1[maxn],n0,n1;

void dec2bin(unsigned long long t){
    for(int i=0;i<n;++i){
        digit[n-i-1]=t%2;
        t=t/2;
    }
}

void countDigit(){
    for(int i=0;i<n;++i){
        if(digit[i]==0){
            digit0[n0++]=i;
        }else{
            digit1[n1++]=i;
        }
    }
}

void greedy(){
    maxv=minv=0;
    int i=0,j=0;
    //反向计算二进制指数值的技巧
    while(i<n0 || j<n1){
        if(i<n0 && (j==n1 || digit1[j]+d>=digit0[i])){
            ++i;
            minv=2*minv;
        }
        else {
            ++j;
            minv=2*minv+1;
        }
    }
    i=j=0;
    while(i<n0 || j<n1){
        if(j<n1 && (i==n0 || digit0[i]+d>=digit1[j])){
            ++j;
            maxv=2*maxv+1;
        }
        else {
            ++i;
            maxv=2*maxv;
        }
    }
}

int main(void){
    while(cin>>n && n){
        cin>>d>>k;
        n0=n1=0;
//        memset(digit,-1,sizeof(digit));
        dec2bin(k);
        countDigit();
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;  //dp[0,0]代表空的序列
        //dp[i,j] 代表i个0和j个1,所以对应数组中序号要减少
        for(int i=0;i<=n0;++i){
            for(int j=0;j<=n1;++j){
                if(i<n0 && (j==n1 || digit1[j]+d>=digit0[i])) dp[i+1][j]+=dp[i][j];  //加入0
                if(j<n1 && (i==n0 || digit0[i]+d>=digit1[j])) dp[i][j+1]+=dp[i][j];  //加入1
            }
        }
        ans=dp[n0][n1];
        greedy();
        printf("Case %d:",++kase);
        cout<<" "<<ans<<" "<<minv<<" "<<maxv<<endl;
    }
}
发布了15 篇原创文章 · 获赞 0 · 访问量 164

猜你喜欢

转载自blog.csdn.net/zpf1998/article/details/104051705
今日推荐