线性dp,优化,库特鸽鸽的时间分配
Problem:D
Time Limit:1000ms
Memory Limit:1024000K
Description
在化工街上,一共有n个房子,每个房子里面住着一个人,分别是库特鸽鸽的n个迷妹。库特鸽鸽十分头疼,因为他业务繁忙,每天只有k的空闲时间能陪他的n个迷妹们。 特别地,对于迷妹i(1<=i<=n),库特鸽鸽花费的时间必须在0到a[i](包括0和a[i])之间。 库特鸽鸽想考考你,恰好花费k时间陪n个迷妹的方案数是多少。 (答案对1e9 + 7取余)
Input
第一行两个整数n,k (1<=n<=100, 0<=k<=100000) 第二行n个整数 a[i] (0<=a[i]<=k)
Output
输出一个整数,代表方案数。
Sample Input
3 4 1 2 3
Sample Output
5
解析:
这是道线性dp的题目,状态转移方程也很容易想到:
f[i][j]+=f[i-1][j-m]
表示前 i 个 a[i] 用时为 j 的方案数
这里的
i :从0道n
j :从0到k
m :从0到a[i]
时间复杂度为O(nkk)
这显然会超时,所以要将的个过程做一个优化
我们将状态转移方程展开得:
f[i][j]=f[i-1][j-0]+f[i-1][j-1]+f[i-1][j-2]+f[i-1][j-3]+........+f[i-1][j-a[i]]
仔细观察不难发现 f[i][j] 实际上就能与 f[i-1][j] 得前缀和
所以定义数组 sum 为f[i-1] 前缀和
则 f[i][j]=sum[j]-sum[max(j-a[i]-1,0)]
这里有一个细节:
f[i][0]=1,但如果 sum[0] 等一 f[i][0] 的话 sum[j]-sum[max(j-a[i]-1,0)] 就永远算不到 f[i][0] 所以我们定义 sum[j+1] 为f[i-1] 的前j个前缀的和
则:f[i][j]=sum[j+1]-sum[max(j-a[i]),0];
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, mod = 1e9 + 7;
int n, k;
int a[105];
LL f[N], sum[N];
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
f[0] = 1;
for (int i = 1; i <= n; i++) {
sum[0] = 0;
for (int j = 0; j <= k; j++) {
sum[j + 1] = (sum[j] + f[j])%mod;
}
for (int j = 0; j <= k; j++) {
f[j] = (sum[j + 1] - sum[max(j - a[i],0)]+mod) % mod;
}
}
cout << f[k] << endl;
return 0;
}