首页 > 代码库 > POJ3252 组合数学

POJ3252 组合数学

POJ3252

问题重述:

求解在区间[start, finish]之间的Round Number的数目。所谓Round Number指的是,二进制表示中0的位数大于等于1的位数的整数。

分析:

1.假如能够分别得到[0, finish] 和 [0, start - 1]区间内的Round Number的数目n1, n2, 那么问题的答案就是n1 - n2. 因此问题可以转化为求解小于n的Round Number的数目。

2.假设整数N的二进制表示为 1,n2,n3,n4,..nk, 则可以把小于N的roundnumber的数目分为两个部分

  1)二进制表示位数小于k的Round Number的个数。

   容易知道位数为j的Round Number的个数为 C[j-1][0] + C[j - 1][1] + ... + C[j - 1][j / 2 - 1]。

  2)位数为k且小于N的Round Number的个数。

  M小于N可以理解为:M和N的二进制表示在出现第一个不同位之前的所有位都相同,且mj = 0, nj = 1,(j表示第一个不同位的序号)。因此小于N的Round Number的数目可以通过如下算法计算:找到N中非零位nj = 1, 令mj=0, 且m1 = n1, m2 = n2,.., mj-1   = nj-1,这样只需要令剩余的k - j位的非零位的数目使M满足Round Number的条件即可。

  例如对于N=1010010, 1)令M = 100xxxx, 那么M的后4位最多只能有2个1,因此满足该形式的M的个数有C[4][0] + C[4][1] + C[4][2]; 2)令M = 101000X, 则满足条件的M的个数为C[1][0] + C[1][1].

根据以上分析即可求解此问题。

AC代码

 1 //Memory: 232K        Time: 16MS 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5  6 using namespace std; 7  8 int c[64][64]; 9 int n[64];10 11 void init_c()12 {13     for (int i = 0; i < 64; i++) {14         c[i][0] = 1;15         c[i][i] = 1;16     }17     for (int i = 2; i < 64; i++)18         for (int j = 1; j < i; j++)19             c[i][j] = c[i - 1][j] + c[i - 1][j - 1];20 }21 22 int a[40];23 int len = 0;24 25 int roundNum (long long n)26 {27     len = 0;28     while(n) {29         a[len++] = n % 2;30         n /= 2;31     }32     int ret = 0;33     34     for (int i = 2; i < len; i++) {35         for (int j = 0; j <= i / 2 - 1; j++) {36             ret += c[i - 1][j];37         }38     }39 40     int cnt = 1;41     for (int i = len - 2; i >= 0; i--) {42         if (cnt > len / 2) break;43         if ( a[i] ) {44             for (int j = 0; j <= len / 2 - cnt; j++)45                 ret += c[i][j];46             cnt++;47         }48     }49 50     return ret;51 }52 53 int main()54 {55     init_c();56     long long s, f;57     cin >> s >> f;58     cout << roundNum(f + 1) - roundNum(s) << endl;59     return 0;60 }