HDOJ 4345 Permutation(分组背包 + 数论)

题意:

给你一个数N(1<=N<=1000),求这么N有多少个不同的旋转长度。旋转长度是指,一个数最少经过多少步可以回到原来的数。

例如 N = 6 时,假如123、45、6分别为三个旋转周期,则旋转顺序为:123456,312546, 231456, 123546, 312456, 231546,123456,旋转长度为6。

当然你也可以12,34,56旋转周期,旋转长度为2。

思路:

即将一个数分成若干份,这若干份不同的最小公倍数的总和,5+4 即5+2^2,最小公倍数为20是一种选择方案

第一组为:2, 2^2, 2^3, ......

第二组为:3, 3^2, 3^3, ......

以此类推,每一组最多只能选择一种解决方案

dp[n] 为填充容量不大于n时,解决发难的总和,若最终选择结果不等于n,则相当于填充了1

其实还可以理解成第0组:1, 11, 111...... 

对于初始化就有,用第0组物品填充,于是dp[0] ~dp[n] = 1

 

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;

const int MAXN = 1001;
__int64 dp[1005];
vector<int> v[200];

bool isprime(int n)
{
    for (int j = 2; j <= sqrt(n*1.0); ++j)
        if (n % j == 0)
            return false;
    return true;
}

int main()
{
    int c = 0;
    for (int i = 2; i < MAXN; ++i)
    {
        if (isprime(i)) 
        {
            v[c].clear();
            for (int j = i; j < MAXN; j *= i)
               v[c].push_back(j); 
            ++c;
        }
    }
    int n;
    while (scanf("%d", &n) != EOF)
    {
        for (int i = 0; i <= n; ++i)
            dp[i] = 1;
        for (int i = 0; i < c; ++i)
            for (int j = n; j >= 1; --j)
                for (int k = 0; k < v[i].size() && v[i][k] <= j; ++k)
                    dp[j] += dp[j-v[i][k]];
        printf("%I64d\n", dp[n]);
    }
    return 0;
}

 

posted @ 2012-11-04 15:07  kedebug  阅读(229)  评论(0编辑  收藏  举报