POJ3252-RoundNumbers-排列组合
当一个数的二进制表示中,0的个数大于或等于1的个数时,叫做RoundNumber。求从S到F两个数(包含)之间的RoundNumber个数。
这类题一般都是先求出0到N的个数,然后两个相减。
由于题目是考虑二进制中01的个数,当位数固定时,很方便计算。于是从位数方面解决问题。
设N表示成二进制的位数为len。把0到N分为两部分。
-位数为[0,len-1]时,可以通过简单的排列组合计算出结果。
-位数为len时,逐位进行分析。假设N是24,表示成二进制是1 1000,1的个数是2,len/2 =2。
最高位一定是1,否则就不是len位了。
第二位是1,如果第二位是0,则后面的三位可以有一个1,也可以没有1,RoundNumber就是C31 +C30 个。
后面的位都是0,这时不能将其替换成1,从0变成1将会比N更大。
于是分两步就求助了0-N的个数。
中间有两个小问题。一个是组合数的计算,我是从kuangbin那里抄的递推写法。关于组合数的计算还要再学习一个,最近数学专题。。。
还有就是边界问题。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; unsigned int S,F; int C[33][33]; void init() { C[0][0]=1; C[1][0]=1;C[1][1]=1; for(int i=2;i<33;i++) { C[i][0]=1; for(int j=1;j<i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j]; C[i][i]=1; } } int getbit(unsigned int x) { for(int i=31;i>=0;i--) { if(x&(1<<i)) return i+1; } } int get1(unsigned int x) { int ans = 0; for(int i=31;i>=0;i--) { if(x&(1<<i)) ans++; } return ans; } unsigned int get0To1(unsigned int x) { int n = getbit(x); int ans = 0; n--; for(int i=1;i<=n;i++) { if(i == 1) continue; int mx = i/2; for(int j=0;j<mx;j++) { ans += C[i-1][j]; } } return ans; } int get0(unsigned int x) { int ans =0; if(x == 0) return 0; for(int i=0;i<32;i++) { if((x&(1<<i)) == 0) ans++; else return ans; } return ans; } unsigned int get1ToX(unsigned int x) { int one = get1(x),n=getbit(x); int mx = n/2,ans=0,cur=1; //printf("x=%d one=%d n=%d\n",x,one,n); if(one <= mx) ans++; for(int i=n-2;i>=0;i--) { if((x&(1<<i)) == 0) { //printf("gg"); continue; } else { for(int j=0;j<=mx-cur;j++) { ans += C[i][j]; } cur++; } } return ans; } int main() { init(); while(~scanf("%d%d",&S,&F)) { if(S > F) swap(S,F); //printf("all1 S=%d F=%d\n",get0To1(S),get0To1(F)); //printf("toX S=%d F=%d\n",get1ToX(S),get1ToX(F)); int ans = (get0To1(F)+get1ToX(F)) - (get0To1(S)+get1ToX(S)); if(get1(S) <= getbit(S)/2) ans++; printf("%d\n",ans); } }
还有就是边界问题了。