ZOJ 3644 Kitty's Game(状态简化 & DP)

来源:互联网 发布:山东省卫生网络直报 编辑:程序博客网 时间:2024/05/17 01:31

题目链接:Click here~~

题意:

给出 n 个点的有向图,每个点分别有一个数字 p[i] ,获得的分数为所经过的点的最小公倍数,且不能出现最小公倍数不变的情况。

起始时主人公在点 1,问走到点 n 时分数为 k 的方案数。

解题思路:

一道需要简化状态的 DP。

虽然原本的有向图并没有保证无环,但是由于 不能出现最小公倍数不变的情况 这个条件,于是本题就可以 dp 了,因为能走的路径一定不会出现环。

令 dp[u][score] 表示 走到点 u 时 分数为 score 的方案数,初始 dp[u][k] = 1。

则有 dp[u][score] = sum{ dp[v][lcm(score,v)] }(<u,v> ∈ G && k%lcm==0)。

但 score 的范围太大导致状态数太多,不能解。其实不难发现其实只有 score 为 k 的因子时,才可能有解,即状态才合法。

故我们可以根据这一点来简化状态,实现方法就是用 map 映射一下,用 dp[u][ M[score] ] 来表示状态。

所以二维只需要开到因子个数的最大值即可,不过我忘记一个数的因子个数是什么级别的了,亲测后发现,100万 大概是 256,10万 大概是 128,。

逆推比较麻烦,所以可以用记忆化搜索写,很方便。

#include <map>#include <vector>#include <stdio.h>#include <string.h>using namespace std;const int N = 2e3 + 5;const int mod = 1e9 + 7;typedef long long LL;inline int gcd(int a,int b){    return b ? gcd(b,a%b) : a;}inline LL lcm(int a,int b){    return (LL)a / gcd(a,b) * b;}int n,k,p[N],dp[N][256];vector<int> g[N];map<int,int> M;int dfs(int u,int score){    int id = M[score];    if(dp[u][id] != -1)        return dp[u][id];    int ret = 0;    for(int i=0;i<(int)g[u].size();i++){        int v = g[u][i];        LL Lcm = lcm(score,p[v]);        if(k % Lcm || Lcm == score && u != 0)            continue;        (ret += dfs(v,Lcm)) %= mod;    }    return dp[u][id] = ret;}int main(){    int m;    while(~scanf("%d%d%d",&n,&m,&k))    {        for(int i=0;i<=n;i++)            g[i].clear();        while(m--){            int u,v;            scanf("%d%d",&u,&v);            g[u].push_back(v);        }        for(int i=1;i<=n;i++)            scanf("%d",&p[i]);        if(k % p[n])            puts("0");        else{            g[0].push_back(1);            M.clear();            int id = 0;            for(int i=1;i*i<=k;i++)                if(k%i == 0){                    M[i] = id++;                    M[k/i] = id++;                }            memset(dp,-1,sizeof(dp));            dp[n][ M[k] ] = 1;            printf("%d\n",dfs(0,1));        }    }    return 0;}