jzoj剪草【DP】

这道题花了我超多时间!!!!
>Description
有N棵小草,编号0至N-1。奶牛Bessie不喜欢小草,所以Bessie要用剪刀剪草,目标是使得这N棵小草的高度总和不超过H。在第0时刻,第i棵小草的高度是h[i],接下来的每个整数时刻,会依次发生如下三个步骤:

(1)每棵小草都长高了,第i棵小草长高的高度是grow[i]。

(2)Bessie选择其中一棵小草并把它剪平,这棵小草高度变为0。注意:这棵小草并没有死掉,它下一秒还会生长的。

(3)Bessie计算一下这N棵小草的高度总和,如果不超过H,则完成任务,一切结束, 否则轮到下一时刻。

你的任务是计算:最早是第几时刻,奶牛Bessie能完成它的任务?如果第0时刻就可以完成就输出0,如果永远不可能完成,输出-1,否则输出一个最早的完成时刻。


>Input
第一行,两个整数N和H。 1 ≤ N ≤ 50,0 ≤ H ≤ 1000000。

第二行,N个整数,表示h[i]。0 ≤ h[i] ≤ 100000。

第三行,N个整数,表示grow[i]。1 ≤ grow[i] ≤ 100000。

>Output
一个整数,最早完成时刻或-1。


>解题思路
使用DP大法。f[i][j]表示的是前i棵草砍j次的最小高度和。
所以它有两种选择:
1第j次砍第i棵草(砍):f[i][j]=f[i-1][j-1]+tt[i]-a[i].g
//这里前i棵草中的每一棵都要生长一次,但是i已经被砍了,所以不用管i,直接把第i个清0

2第j次不砍第i棵草(不砍):f[i][j]=f[i-1][j]+a[i].yd+a[i].g*j
//由于第i棵草不砍,so这里的数是从f[i-1][j]过来的,前面的数(前i-1个数总共j次)都已经在之前处理过了,所以只用处理第i个数(加上第i个数的初始与生长总数)

=====================================================================================
a[i].yd为第i棵草的初始长度,g为生长速度。
tt为前i棵草的生长速度总和


>代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct ooo
{
	int yd,g;
}a[55];
int n,s,f[55][55],ans,tt[55];
bool lil(ooo aa,ooo bb)
{
	return aa.g<bb.g;
}
int main()
{
	scanf("%d%d",&n,&s);
	for(int i=1;i<=n;i++)
	  scanf("%d",&a[i].yd);
	for(int i=1;i<=n;i++) 
	  scanf("%d",&a[i].g);
	sort(a+1,a+1+n,lil); //把结构体a按照g从小到大进行排序,先删去生长慢的,在删去生长快的
	for(int i=1;i<=n;i++)
	{
		f[i][0]=f[i-1][0]+a[i].yd; tt[i]=tt[i-1]+a[i].g;
		//在上文说明过
	}
	if(f[n][0]<=s)
	{
		printf("0");
		return 0;
	}
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=n;j++)
		f[i][j]=min(f[i-1][j]+a[i].yd+a[i].g*j,f[i-1][j-1]+tt[i]-a[i].g);
	for(ans=1;ans<=n;ans++)
     if(f[n][ans]<=s)
	 {
		printf("%d",ans);
		return 0;
	 }
	printf("-1");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值