poj3252(组合数)
Posted xiongtao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj3252(组合数)相关的知识,希望对你有一定的参考价值。
题目链接:http://poj.org/problem?id=3252
题目意思:给出两个整数s,f,问区间[s,f]中 "round number"的个数。(1<=s<f<=2000000000)
"round number"定义:二进制中0的个数大于等于1的个数。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; int c[33][33]={0}; void init()//组合数打表 { c[0][0]=1; for(int i=1;i<33;i++) { c[i][0]=1; for(int j=1;j<=i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1]; } } int count(int n)//寻找比n小符合条件个数 { int d=0,zero=0,res=0; bool m[33]={0}; while(n)// 转化为二进制,d计算 位数,m[]存每一位是0还是1 { m[++d]=n%2; n/=2; } for(int i=2;i<d;i++)//计算二进制位数小于d的情况,(省略了位数为1,因为1不符合条件) { for(int j=(i+1)/2;j<i;j++) res+=c[i-1][j]; } for(int i=d-1;i>=1;i--)//计算二进制位数等于于d的情况 { if(m[i]) { for(int j=(d+1)/2-(zero+1);j<i;j++)//遍历每种小于的情况 res+=c[i-1][j]; } else zero++; } return res; } int main() { int s,e; init(); while(cin>>s>>e) { cout<<count(e+1)-count(s)<<endl; } return 0; }
思路:区间问题,容易想到写个函数找到1~n的符合条件的数量,这样用1~f中的个数减去1~s中的个数就是答案。
因为这里写的函数 count(int n) 是找到比n小的 "round number"有多少个。所以是用count(f+1)-count(s),搞清楚区间闭合关系。
下面就是找 "round number"的工作了:
我们会发现当数字转为二进制时,位数小于该数的十进制一定比它小(这是肯定的,废话一句),那么可以分成两部分:
一、二进制位数小于时
二、二进制位数相同时
情况一:符合条件我们需要以下几点:
1、首位一定要是1,(怎么可能不是。。。)
2、0的个数是位数的一半以上,包括一半。
比如:位数是n时,那我们考虑n-1低位必须取一半及以上((n+1)/2)个0。
选择的方式自然就想到组合数,即从n-1个(除掉首位一定是1)选(n+1)/2 ~ n-1的0有多少种情况。
代码:
void init()//组合数打表 { c[0][0]=1; for(int i=1;i<33;i++) { c[i][0]=1; for(int j=1;j<=i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1]; } } while(n)// 转化为二进制,d计算 位数,m[]存每一位是0还是1 { m[++d]=n%2; n/=2; } for(int i=2;i<d;i++)//计算二进制位数小于d的情况,(省略了位数为1,因为1不符合条件) { for(int j=(i+1)/2;j<i;j++) res+=c[i-1][j]; }
情况二:计算相同位数时,因为函数 count (int n) 计算的是小于n有多少个。
即找出二进制每一位为1时,变成0(即小于它)的情况找出。(当然第一位除外,变成0就不是d位数的二进制了,变成d-1位)
举例:二进制 11011
1) 确定第二位是1 11011 10 xxx(一定比它小) c[3][2]+c[3][3](即剩下不确定的三位要二个0或三个0,这样0的个数才大于1)
2) 确定第四位是1 11011 1001x c[1][1]一定要是0才可以
3) 确定第五位是1 11011 已经找不到了(所以说这个函数是找到比11011小的符合条件的个数)
很明显这也是一个组合数,必须确保0比1多的,要用一个变量zero记录确定这位之前0的个数,那么剩下的0的个数为
(n+1)/2-(zero+1)到未确定位数,为什么减(zero+1),是确定这位要变成0.
代码:
for(int i=d-1;i>=1;i--)//计算二进制位数等于于d的情况 { if(m[i]) { for(int j=(d+1)/2-(zero+1);j<i;j++)//遍历每种小于的情况 res+=c[i-1][j]; } else zero++; }
以上是关于poj3252(组合数)的主要内容,如果未能解决你的问题,请参考以下文章