LightOJ 1145 Dice (I) dp

来源:互联网 发布:网络信息化建设 编辑:程序博客网 时间:2024/05/19 11:47

题目链接:LightOJ 1145

题意:

有n个骰子,每个骰子有k个面,每个面有一个权值,为该面的下标,现在用用n个骰子凑出面值s,问有多少种方法。

思路:

设dp[i][j]表示第i个骰子凑出了j有dp[i][j]种方法。
那么dp[i][j] = dp[i-1][j-1] + dp[i-1][j-2] + … + dp[i-1][j-k]
那么状态有n*s个,转移花费k,显然会超时。

观察发现:
dp[i][j] = dp[i-1][j-1] + dp[i-1][j-2] + … + dp[i-1][j-k]
dp[i][j-1] = dp[i-1][j-2] + dp[i-1][j-3] + … + dp[i-1][j-k-1]

联立一下得到:
dp[i][j] = dp[i][j-1] - dp[i-1][j-k-1] + dp[i-1][j-1]

这样转移就是O(1)了。

然后MLE了。。。
n*s = 1500W * 4byte = 60 000 000 byte = 60MB > 32MB
再次观察发现每次只用到dp[i]和dp[i-1],这样可以用dp[2][s]大的数组滚动处理就可以了。

代码

#include <iostream>#include <cstring>#include <cstdio>using namespace std;long long dp[2][15005];const int mod = 100000007;int main(){    int t, n, k, s, Case=0;    scanf("%d", &t);    while(t--){        scanf("%d%d%d", &n, &k, &s);        for(int i=1; i<=k; ++i) dp[1][i] = 1;        for(int i=k+1; i<=s; ++i) dp[1][i] = 0;        for(int i=2; i<=n; ++i){            for(int j=1; j<=s; ++j){                if(j-k-1>=0)                    dp[i&1][j] = ((dp[i&1][j-1] + dp[(i-1)&1][j-1])%mod + mod - dp[(i-1)&1][j-k-1]) % mod;                else                    dp[i&1][j] = ((dp[i&1][j-1] + dp[(i-1)&1][j-1])%mod + mod - dp[(i-1)&1][0]) % mod;            }        }        printf("Case %d: %lld\n", ++Case, dp[n&1][s]);    }    return 0;}
原创粉丝点击