这题目做了两个小时,调试的头都晕了。。。
题型是数位DP中很常见的,给一个区间[l,r]求区间[l,r]中的 符合题目要求的数的个数,本题求的 是 区间中 的数 它的二进制中0的个数大于等于1的个数的 这些数的个数,不好意思表达能力有点差。。。例如[1,4]答案是2, 因为 2的二进制是10,0的个数大于等于1的个数,4的二进制是100,0的个数大于1的个数,所以答案是两个
好了第一反应肯定是 设定一个函数 f(r) - f[l - 1]就是答案,那么显然这个函数f(r)的意思就是 1 到 r之间符合要求的数的个数,很容易想到数位DP,但是真心不好推啊,由于推数位DP的过程中对每个数的二进制 都画了一下,后来发现了 组合数学其实也是可以做的,
首先每一个数化为二进制数 第一位肯定是1,那么剩下的位数中 让一定的位数 上的数字为0就可以达到目的,例如 1 {}{}{} 其中{}表示空位,剩下的三个空位中至少让两个为0即可,那么答案其实就是 C(3,2) + C(3,3),跟组合数联系上了,那么就好办了
首先 把一个数m处理成二进制数,假设长度为n,那么 所有 长度为[1,n-1]的数 中 合法的 个数很容易就求出来了,直接扫一遍 然后判断它的长度再求组合数即可
接下来的部分就是分析 长度等于n的 且 大小必须小于等于m的 数中 合法的个数,有点麻烦,就是扫他的二进制数,若扫到的当前位上的数字为1,那么就令这个位置上的数为0,这样当前二进制表示的数 肯定是小于m的,这样就对于还未扫到的位 上的数 进行组合数求解答案即可,具体见代码中的注释部分
郁闷的是,我做的时候脑子有点糊涂,做到一半曲解了题目的意思,求的是 [l,r]中 数的二进制中1的个数大于0的个数的 这些数的个数,还好答案直接由总数减一下就可以了。。。不然就惨了。。。
int s,e; int bin[33]; const int MAXN = 33; int C[33+1][33+1]; int cnt = 0; void Initial() { int i,j; for(i=0; i<=33; ++i) { C[0][i] = 0; C[i][0] = 1; } for(i=1; i<=33; ++i) { for(j=1; j<=33; ++j) C[i][j] = (C[i-1][j] + C[i-1][j-1]); } } void init() { } bool input() { while(cin>>s>>e) { return false; } return true; } void Binary(int n) { memset(bin,0,sizeof(bin)); cnt = 0; while(n) { bin[cnt++] = n&1; n >>= 1; } } int slove(int n) { if(n == 0)return 0; int ret = 0; Binary(n); for(int i=cnt - 1;i>0;i--) {//统计所有二进制数长度小于n的,可以自由组合数 int tmp = (i - 1); if(tmp&1)tmp = (tmp>>1) + 1; else tmp >>= 1; for(int j=tmp;j<=i - 1;j++) { ret += C[i - 1][j]; //cout<<C[i - 1][j]<<endl; } } int mark1 = 1,mark2 = 0; for(int i=cnt - 1;i>0;i--) {//统计长度与n相同的且小于等于n的 if(bin[i - 1]) { int tmp; if(cnt&1)tmp = (cnt + 1)>>1; else tmp = (cnt>>1) + 1;//长度为cnt的至少需要多少个1 tmp = tmp - mark1;//减去前面已经扫到的1的个数,当前至少还需几个1 if(tmp < 0)tmp = 0;//有可能前面已经超过了需要的1的个数,这里需要注意,置0,WA出翔 for(int j= tmp;j<=i - 1;j++) ret += C[i - 1][j]; mark1++;//这个必须放最后,因为当前为0,不是必须为1,调试了半年 } else mark2++; } ret += mark1>mark2?1:0;//写到最后发现我无意曲解了题意, ret = n - ret;//我求的是一个数的二进制中1大于0的个数,那么好办求反就可以了 return ret; } void cal() { cout<<slove(e) - slove(s - 1)<<endl; } void output() { } int main() { Initial(); while(true) { init(); if(input())return 0; cal(); output(); } return 0; }
POJ3252 Round Numbers 组合数学||数位DP
原文:http://blog.csdn.net/yitiaodacaidog/article/details/44815075