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;

}

 

你可能感兴趣的:(IO)