【數論】【搜索】【SCOI2009】遊戲

Description
windy学会了一种游戏。对于1到N这N个数字,都有唯一且不同的1到N的数字与之对应。最开始windy把数字按顺序1,2,3,……,N写一排在纸上。然后再在这一排下面写上它们对应的数字。然后又在新的一排下面写上它们对应的数字。如此反复,直到序列再次变为1,2,3,……,N。 如: 1 2 3 4 5 6 对应的关系为 1->2 2->3 3->1 4->5 5->4 6->6 windy的操作如下 1 2 3 4 5 6 2 3 1 5 4 6 3 1 2 4 5 6 1 2 3 5 4 6 2 3 1 4 5 6 3 1 2 5 4 6 1 2 3 4 5 6 这时,我们就有若干排1到N的排列,上例中有7排。现在windy想知道,对于所有可能的对应关系,有多少种可能的排数。
Input
包含一个整数,N。
Output
包含一个整数,可能的排数。
Sample Input
【输入样例一】
3

【输入样例二】
10

Sample Output
【输出样例一】
3

【输出样例二】
16

【数据规模和约定】
30%的数据,满足 1 <= N <= 10 。
100%的数据,满足 1 <= N <= 1000 。
這是一道數論+記憶化搜索的題目。

遠問題可以轉化為一下問題:
已知a1+a2+a3+...+am = N,求Lcm(a1, a2, a3, ..., am)的所有可能性。

做法如下:枚舉從1到n之間的所有素數,再枚舉每個素數的方冪。由唯一分解定理可知,所枚舉的每一個不同的對象,都對應一個不同的解,再將這些解累加即可。

由於數據較大,必須使用記憶化搜索,並且要用64位整型。
Accode:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using std::cin;
using std::cout;
typedef long long int64;
const int maxN = 1010;
const int maxM = 210;

int prime[210];
int64 f[210][1010];
int n;

inline void mkprime(int Lim)
{
    prime[0] = 2; prime[1] = 3; prime[2] = 5;
    int Last = 7;
    for (int i = 3; Last <= Lim; ++i, ++Last)
        for (; ; ++Last)
        {
            bool flag = true;
            for (int j = 0; prime[j]
                 * prime[j] <= Last; ++j)
                if (Last % prime[j] == 0)
                {
                    flag = false;
                    break;
                }
            if (flag)
            {
                prime[i] = Last;
                break;
            }
        }
} //製作一張素數表。

int64 Find(int i, int n)
{
    if (prime[i] == -1 || prime[i] > n)
	//處理邊界。
        return f[i][n] = 1;
    if (f[i][n] > -1) return f[i][n];
	//記憶化搜索。
    f[i][n] = 0;
    for (int tmp = prime[i];
         tmp <= n; tmp *= prime[i])
        f[i][n] += Find(i + 1, n - tmp);
	//該素數被選,則枚舉它的次數。
    return f[i][n] += Find(i + 1, n);
	//統計該素數未被選的情況。
}

int main()
{
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    cin >> n;
    memset(f, 0xff, sizeof(f));
    memset(prime, 0xff, sizeof(prime));
    mkprime(n);
    cout << Find(0, n);
    return 0;
}

第二次做,改进递推版,并且加上就地滚动,速度更快。

#include <fstream>
#include <cstring>
#include <cstdlib>

typedef long long int64;
const int maxP = 200;
const int maxN = 1010;

int64 f[maxN];
int prime[maxP];
int n, tot;

bool is_prime(int x)
{
    for (int i = 0; prime[i]
    * prime[i] <= x; ++i)
        if (x % prime[i] == 0)
            return false;
    return true;
}

void mkprime()
{
    prime[0] = 2;
    for (int i = 3; i < n + 1; ++i)
        if (is_prime(i))
            prime[++tot] = i;
    return;
}

int main()
{
    std::ifstream cin("game.in");
    cin >> n;
    cin.close();
	mkprime();
	for (int j = 0; j < n + 1; ++j)
        f[j] = 1;
	for (int i = 0; i < tot + 1; ++i)
	    for (int j = n; j > -1; --j)
            for (int k = prime[i];
            k <= j; k *= prime[i])
                f[j] += f[j - k];
    std::ofstream cout("game.out");
    cout << f[n] << std::endl;
    cout.close();
	return 0;
}

你可能感兴趣的:(游戏,input,output)