POJ3252

题目链接:https://vjudge.net/problem/POJ-3252

知识点:组合数的一个递推式:C(i,j)=C(i-1,j-1)+C(i-1,j) (易证)

解题思路: 

  要求有多少个round number小于或等于某一个数S (其二进制位数为t),我们分三类进行:

  1、看看S本身是不是round number。对于该类,只需将S转换成二进制并记录其0、1个数即可验证;

  2、找出二进制位数小于 t 的所有round number。我们给出一个int cul (int n)函数来求二进制长度为n的所有round num,首先需要知道的是:其第一位必定为1。我们要保证0的个数多于或等于1的个数,则我们可以得出如下公式:若n为奇数,则cul(n) = C(n-1,n/2+1) + C(n-1,n/2+2) + ... + C(n-1,n-1); 若n为偶数,则cul(n) = C(n-1,n/2) + C(n-1,n/2+1) + ... + C(n-1,n-1), 另外,由于当n=1,我们会求到 C(0,1),这显然不妥,于是我们在函数中加上条件:if(n<=1)    return 0;  。则该类round number的总数为: cul(t-1) + cul(t-2) + ... + cul(1)。

  3、现在需要找出二进制位数等于 t 的 round number。我们用 cnt1 和 cnt0 两个变量记录S中0和1个数的实时变化。首先,每个数的第一个数都是1,则初始化cnt1=1,cnt0=0.接下来我们遍历给出的数的后面的每一位,如果该位上的数为0,则只需cnt0++即可,因为我们能且只能在该位上放置0;如果该位上的数是1,则我们可以先将该位数看为0,"暂时" (注意,只是暂时的,等下还要把cnt0恢复回去)地把cnt0看为 cnt0+1 ,那么后面的数字肯定是无论我们怎么取值,都肯定小于S,这就有点像第2类,求二进制位数小于某个数的所有round number, 但是在这里还需要考虑 cnt1 和 cnt0 需要保证0的总个数大于或等于1的总个数,我们设0的个数为 j ,后面的二进制位数为 i ,则在此处的round number数为:C(i-1,i-1) + C(i-1,i-2) + ... + C(i-1,j) . 需要保证 j + cnt0 >= i - j + cnt1,这里的cnt0其实就是cnt0 + 1,详情请参考代码。最后别忘了cnt1++。

AC代码:

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 int C[35][35];
 5 void init(){
 6     C[0][0]=1;
 7     C[1][1]=C[1][0]=1;
 8     for(int n=2;n<35;n++){
 9         C[n][0]=C[n][n]=1;
10         for(int m=1;m<n;m++)
11             C[n][m]=C[n-1][m-1]+C[n-1][m];
12     }
13 }
14 int cul(int n){
15     if(n<=1)    return 0;
16     int s,ans=0;
17     if(n%2) s=n/2+1;
18     else    s=n/2;
19     for(int i=s;i<n;i++)
20         ans+=C[n-1][i];
21     return ans;
22 }
23 int finds(int num){
24     if(num<=1)  return 0;
25     int tmp=num,dig[50];
26     int len,lastone;
27     int c1=1,c0=0;
28     for(len=0;tmp>0;tmp=tmp>>1,len++){
29         if(len>0){
30             if((tmp<<1)<lastone){
31                 dig[len]=1;
32                 c1++;
33             }
34             else{
35                 dig[len]=0;
36                 c0++;
37             }
38         }
39         lastone=tmp;
40     }
41     dig[len]=1;
42     int ans=0;
43     if(c1<=c0)  ans++;
44 
45     for(int i=1;i<=len-1;i++)
46         ans+=cul(i);
47 
48     int cnt0=0,cnt1=1;
49     for(int i=len-1;i>0;i--){
50         if(dig[i]==0)   cnt0++;
51         else{
52             for(int j=i-1;j>=0;j--){
53                 if(2*j<i+cnt1-2-cnt0) break;
54                 ans+=C[i-1][j];
55             }
56             cnt1++;
57         }
58     }
59     return ans;
60 }
61 int main()
62 {
63     init();
64     int s,f;
65     scanf("%d%d",&s,&f);
66     printf("%d\n",finds(f)-finds(s-1));
67     return 0;
68 }
View Code

 

转载于:https://www.cnblogs.com/Blogggggg/p/7227279.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值