线性dp,优化,库特鸽鸽的时间分配

Contest (nefu.edu.cn)

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值