正文

以空间换时间算法集锦(3)2007-04-20 14:41:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/goal00001111/25030.html

分享到:

4.满足条件的m的个数 给出一个数字n和k,请找出有多少个不同的整数m满足: 1) m<2^n 2) m>=0 3) |f(m,n)|<=k 其中f(x,n)的定义如下: f(0,n)=n; f(x,n)=f([x/2],n),x为偶数 f(x,n)=f([x/2],n)-2,x为奇数   Input 每组一行,分别给定n,k(1<=n<=62,2<=k<=5)   Output 每组一行,给出满足条件的m的个数   Sample Input 1 2 4 3   Sample Output 2 14   主函数: #include <iostream> #include <time.h>   using namespace std;   int Fun(int n, int k);   int main() {     time_t startTime;     time_t endTime;     time(&startTime);       for (int i=1; i<=20; i++)         for (int j=2; j<=5; j++)             cout << i << "," << j << ": " << Fun(i, j) << "\t";     cout << endl;           time(&endTime);     cout << difftime(endTime, startTime) << endl;       getchar();     return 0; }   方法一:最直接的算法,根据题意递归求解。 int f(int x, int n) {     if (x == 0)         return n;     if ((x&1) == 0)           //x为偶数         return f(x/2, n);     else                    //x为奇数            return f(x/2, n) - 2; }   int Fun(int n, int k) {     long long max = 1<<n;     long long s = 0;         for (int m=0; m<max; m++)     {         if (abs(f(m, n)) <= k)             ++s;     }     return s; }   方法二:以空间换时间,把所有的f(m, n)值存储到一个数组中,然后查表;由于max的值太大了,不可能建立这么大的数组, 但是可以建立一个较小的数组(长度为400000), 存储对应的f(m, n)值,当m较小时,可以直接查表; 当m >= M时,递归减小m的值,直到m < M ,然后查表。  void Table(int a[], int max, int n)//建立一个表,把所有的f(x, n)值存储到一个数组中 {     a[0] = n;     a[1] = n - 2;     for (int i=2; i<max; i++)     {         if ((i&1) == 0)             a[i] = a[i/2];         else             a[i] = a[i/2] - 2;     } }   long long Fun(int n, int k) {     const int M = 400000;     int a[M];    Table(a, M, n);//建表         long long max = 1<<n; //2^n     long long s = 0;         if (max < M)//当m较小时,可以直接查表     {         for (int m=0; m<max; m++)         {             if (abs(a[m]) <= k)                 ++s;         }     }     else//当m >= M时,递归减小m的值,直到m < M ,然后查表     {         for (int m=0; m<M; m++)//先计算前面小于M的部分         {             if (abs(a[m]) <= k)                 ++s;         }         long long t;         int f;         for (long long m=M; m<max; m++)//计算大于M的部分         {             f = 0;             t = m;             while (t >= M)//递归减小m的值,累积奇数的个数,如果是奇数要减去2             {                 if ((t&1) == 1)                     ++f;                 t >>= 1;             }             f = a[t] - f * 2;//查表             if (abs(f) <= k)                 ++s;         }     }         return s; }   补充一个算法: 这是http://www.programfan.com/club/的sarrow得到的算法。 由        | n;                 x == 0 f(x, n) = | f([x / 2], n);     x even number           | f([x / 2], n) - 2; x odd number 观察x的二进制数,用ones_in_x表示x的二进制数中'1'的个数,zero_in_x表示x的二进制数中'0'的个数, 可以得到 f(x, n) = n - 2 * ones_in_x         = n - 2 * (n - zero_in_x)         = 2 * zero_in_x - n 所以|f(m,n)|<=k 就是 | 2 * zero_in_m - n | <= k 即     -k <= 2 * zero_in_m - n <= k 也即 (n - k) / 2 <= zero_in_m <= (n + k) / 2 又  0 <= m < 2^n 即满足条件的m,其二进制数中'0'的个数应该大于等于i,小于等于j,其中       i = max{(n - k) / 2, 0};对(n - k) / 2应该四舍五入       j = max{(n + k) / 2, n}; 所以求满足条件的m的个数,相当于解一个排列组合题目:分别求从n个'0'取出k个'0'的取法, 其中(i<=k<<j),再求所有取法之和, 即  set_of_m = c(i, n) + ... + c(j, n);   int Gcd(int m, int n)//求最大公约数 {     int temp;         while (n > 0)     {         temp = m % n;         m = n;         n = temp;     }     return m; }   long long Combination(int m, int n)//求组合C(m, n),注意不能直接按定义求,否则会溢出,先约分再除 {       int *pm = new int[m];     int *pn = new int[m];         for (int i=0; i<m; ++i)         pm[i] = i + 1;     for (int i=0; i<m; ++i)         pn[i] = n - i;         int g;     for (int i=0; i<m; ++i)         for (int j=0; j<m; ++j)         {             g = Gcd(pm[i], pn[j]);//求最大公约数             pm[i] /= g;             pn[j] /= g;         }             long long numerator = 1;     long long denominator = 1;         for (int i=0; i<m; ++i)         numerator *= pn[i];     for (int i=0; i<m; ++i)         denominator *= pm[i];             return numerator / denominator; }   long long Fun(int n, int k) {       int low  = (n < k) ? 0 : ((((n-k)&1) == 0) ? (n-k)/2 : (n-k)/2 + 1);     int high = (n < k) ? n : (n+k)/2;     long long s = 0;         for (int i=low; i<=high; ++i)         s += Combination(i, n);       return s; }

阅读(4790) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册