题目大意:
有N个骰子,每个骰子有K个面,分别标号1~K,设每个骰子向上的面的值为fi,如果sum(fi)等于S,那么获得一个分数sco=mult(fi)。
sum(fi)=f1+f2+...+fn
mul(fi)=f1*f2*...*fn
求所有mul(fi)的和。
时间限制为2s
N (1 ≤ N ≤ 1000) K (1 ≤ K ≤ 1000) S (0 ≤ S ≤ 15000).
题目思路:
要求所有N个骰子的sum(fi)为S的所有mul(fi)的和,设为ans[n][s]。
显然有ans[n][s]=ans[n-1][s-1]+2*ans[n-1][s-2]+...+k*ans[n-1][s-k]
可以把上式作为状态转移方程:
dp[i][j] = dp[i-1][j-1] + 2*dp[i-1][j-2] + ... + k*dp[i-1][j-k]
如果用该转移方程,不难判断复杂度为O(n*k*s),肯定是超时的。
其实我们仔细观察转移方程的右边可以看成:
= dp[i-1][j-1] + dp[i-1][j-2] + ... + dp[i-1][j-k]
+ dp[i-1][j-2] + dp[i-1][j-3] + ... + dp[i-1][j-k]
.
.
.
+ dp[i-1][j-k]
我们设sum[i][j] = dp[i][j-1] + dp[i][j-2] + ... + dp[i][1]
那么转移方程的右边又可以看成:
= sum[i-1][j-1] - sum[i-1][j-k-1]
+ sum[i-1][j-2] - sum[i-1][j-k-1]
.
.
.
+ sum[i-1][j-k] - sum[i-1][j-k-1]
我们再设ssum[i][j] = sum[i][j] + sum[i][j-1] + ... + sum[i][1]
这样转移方程的右边又可以看成
= ssum[i][j-1] - ssum[i][j-k-1] - k*sum[i][j-k-1]
这样我们就把O(k)求dp[i][j],转为了O(1),而算法的整体复杂度也降为了O(n*s),这样就不会超时咯。
但是空间复杂度也是O (n*s)的,但是我们发现,求i只需要知道i-1,所以我们可以用滚动数组来处理,这样空间复杂度就降到了O(2*s)。
需要注意的是j-k-1<1的时候,转移方程略有区别,不过也只是小细节的问题,很容易处理。
代码:
[cpp]
#pragma comment(linker, "/STACK:102400000,102400000")
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include