LightOJ 1145 Dice (I) dp

题目链接: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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值