zoukankan      html  css  js  c++  java
  • 二进制上的数位dpPOJ 3252

    Round number POJ - 3252

      题目大意:一个"round number" 数的定义是,将它转化成2进制后,0的个数大于等于1的个数,要求的就是在[s,f]范围内"round number"的个数

      和之前的数位dp不同的是,这题是对二进制的数位进行dp,所以就存在着一个限制,前面有没有存在1,如果前面没有1.那么像00001,就是1,前面的0是不计数的,所以重点就在于考虑这个dfs时就得分前面有没有1,如果没有那么0就不计数,反之我们记录0和1的差值,取0加1,取1减1,不过需要注意的是,差值可能是负数,所以要转正数,详情见代码

     1 #include<cstdio>
     2 #include<cstring>
     3 #define ll long long
     4 const int N=50;
     5 int up[N];
     6 ll n,s,f,dp[N][2*N][2];//dp[i][j][k]代表当前为第i位,前面0和1的差值为j-N,前面有没有1的结果
     7 //例如dp[4][50][1]就代表第4位,在第4位前面0和1差值为0,并且前面有1的结果数 
     8 ll dfs(int p,int d,bool is1,bool isu)//p 位数,d 0和1的差值,is1 该位前面有没有1 isu有没有上限 
     9 {
    10     if(!p)//边缘状态,0大于等于1(d>=0)返回1,反之返回0 
    11         return d>=0;
    12     if(-d>p)//小剪枝,如果剩下位数全是0加起来都没有1多,返回0 
    13         return 0;
    14     if(!isu&&dp[p][d+N][is1]!=-1)
    15         return dp[p][d+N][is1];
    16     ll ans=0;
    17     for(int i=0;i<=(isu ? up[p] : 1);i++)
    18     {
    19         if(is1)//当前面有1正常记录0的差值 
    20             ans+=dfs(p-1,(i ? d-1 : d+1),is1||i,isu&&i==up[p]);
    21         else//当前面没有1,0不计,1记-1 
    22             ans+=dfs(p-1,-i,is1||i,isu&&i==up[p]);
    23     }
    24     if(!isu)
    25         dp[p][d+N][is1]=ans;
    26     return ans;
    27 }
    28 ll solve(ll x)
    29 {
    30     int num=0;
    31     while(x)
    32     {
    33         up[++num]=x&1;
    34         x>>=1;
    35     }
    36     return dfs(num,0,0,1);
    37 }
    38 int main()
    39 {
    40     ll s,f;
    41     memset(dp,-1,sizeof(dp));
    42     while(~scanf("%lld%lld",&s,&f))
    43         printf("%lld
    ",solve(f)-solve(s-1));
    44     return 0;
    45 }
    RNGRNGRNG

      关于结果的存储,不同的想法具体的dp还有也不同,有些是dp[i][j][k]是当前是第i位,前面0的个数为j,前面1的个数为k的结果有多少个,不过大体的思路还是相同的

      除数位dp外,还有一个组合数学的解法,不过相比大佬们,我的思路有点麻烦,详情见代码

     1 #include<cstdio>
     2 #include<cstring>
     3 #define ll long long
     4 ll n,m,c[36][36]={1},a[36]={0};//c组合数,a[i]长度为i,首位1在第i位存在RN的个数 
     5 //比加a[1]保存1,a[2]保存10,11,a[3]保存100,101,110,111,中满足RN的个数
     6 //所以a[1]=0,a[2]=1,a[3]=1 
     7 int up[36]={1};
     8 void init()
     9 {
    10     for(int i=1;i<=32;i++)
    11     {
    12         c[i][0]=1;
    13         for(int j=1;j<=i;j++)
    14         {
    15             if(j<=i/2)
    16                 c[i][j]=c[i-1][j-1]+c[i-1][j];
    17             else
    18                 c[i][j]=c[i][i-j];
    19         }
    20         for(int j=i-1;j>=(i+1)/2;j--)//首位i是1,剩下i-1位0可以i-1个,
    21         //i-2个,假设最少取j个满足要求,那么j>=(i+1)/2(0的数目大于等于总数目的一半) 
    22             a[i]+=c[i-1][j];
    23     }
    24 }
    25 ll solve(ll x)
    26 {
    27     int num=0;
    28     while(x)
    29     {
    30         up[++num]=x&1;
    31         x>>=1;
    32     }
    33     ll ans=0;
    34     for(int i=1;i<num;i++)
    35         ans+=a[i];
    36     //假如x转化成2进制是1101
    37     //先算上1,10~11,100~111的里的答案 
    38     if(num>1)//先把1000算上 
    39         ans++;
    40     int z=0;//记录第i位前0的个数 
    41     //再来计算1000~1101之间的答案,down代表有没有计算到下界
    42     //一开始的下界就是1000                                   
    43     for(int i=num-1,down=1;i>=1;i--)
    44     {
    45         if(up[i])//当存在1时,例如第二位就是1,我们就求的是1000~1100的答案 
    46         {
    47             if(down)//因为之前已经算过1000了,所以这里减去
    48                 ans--,down=0;
    49             for(int j=i-1;z+j+1>=(num+1)/2&&j>=0;j--)//假设第i位是0,前面的0加上
    50             //第i位再加上后面取的j个0,总的0的个数要大于等于总个数 
    51                 ans+=c[i-1][j];
    52             if(i-1+z>=(num+1)/2)//假设后面的i-1全是0,0的个数大于等于总个数 
    53                 ans++,down=1;//那么1100可以作为下界 
    54         }
    55         else
    56             z++;
    57     }
    58     return ans;
    59 }
    60 int main()
    61 {
    62     init();
    63     while(~scanf("%lld%lld",&n,&m))
    64         printf("%lld
    ",solve(m)-solve(n-1));
    65     return 0;
    66 }
    WEWEWE
  • 相关阅读:
    JavaScript的alert()突然出现中文乱码
    对ie CSS hack总结和理解
    getBoundingClientRect()获取元素相对浏览器视窗的坐标值
    typeof和instanceof的区别
    转载:Dos命令行下,关闭占用80端口的进程 (~~~这个比较实用!)
    关于JS浮点数计算误差问题和二进制浮点数表示法的思考
    callee与caller,apply与call的区别和作用
    [Javascript权威指南笔记01]后自增/后自减运算符的副作用 和 运算符的结合性
    转载:关于zindex的那些事(~~~比较理论的描述 堆栈上下文)
    西游东去 (~~创意?创新?恶搞?不置可否,不过有点意思)
  • 原文地址:https://www.cnblogs.com/LMCC1108/p/10458148.html
Copyright © 2011-2022 走看看