正文

以空间换时间算法集锦(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;

}

阅读(4647) | 评论(0)


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

评论

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