题目链接:入门OJ3793
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。
第二行,N个整数,表示h[i]。0 ≤ h[i] ≤ 100000。
第三行,N个整数,表示grow[i]。1 ≤ grow[i] ≤ 100000。
1 ≤ N ≤ 50,0 ≤ H ≤ 1000000。
Output
一个整数,最早完成时刻或-1。
Sample Input
3 16
5 8 58
2 1 1
Sample Output
1
到了第1时刻,三棵小草的高度依次是7,9,59。如果奶牛Bessie把高度是59的小草剪掉
那么三棵小草高度是7+9+0=16,任务完成
思路:首先用贪心的思维,首先想到的第一种贪心思路肯定是先剪长得高的草,但是这样一定是正确的吗?可以证明这个是错的,例如:
3 14
7 8 9
1 1 3
可以推出先剪掉高为9的草,必然不会得到最优解
正确的贪心思路应该是先剪掉成长速度慢的,如果成长速度一样就先剪掉高的
贪心思路想出来了,再来想DP
首先想想他的状态,由题意可知,状态为剪草的次数,那么剪草次数的范围是多少呢。
用贪心的思维,如果同样一棵草你剪掉多次,有效的永远都是最后一次,所以一棵草只能剪一次,那么可以得到次数就是草的数量。
状态得到了,下面来找状态转移方程,可想对于每一颗草,可以选择剪与不剪,是不是脑子里面有一种熟悉的感觉,其实这就是一个稍微复杂点的01背包问题。下面写出状态转移方程
定义dp[i][j] i为第i棵草,j为剪草的次数;
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+a[i].h+j * a[i].grow);1=< i <=n, 1=< j <= i
a[i].h+j * a[i].grow 为第j次你剪掉的第i棵草的高度
参考代码:
#include<bits/stdc++.h>
#define LL long long
#define Max 100005
#define Mod 1e9+7
const LL mod=1e9+7;
const LL inf=0x3f3f3f3f;
using namespace std;
struct node
{
int h,grow;
bool operator < (const node a)const
{
if(a.grow==grow)
return h>a.h;
return grow<a.grow;
}
} a[55];
int dp[55][55];
int n,h;
int main()
{
scanf("%d%d",&n,&h);
int sumh,sumg;
sumg=sumh=0;
for(int i=1; i<=n; i++)
scanf("%d",&a[i].h),sumh+=a[i].h;
for(int i=1; i<=n; i++)
scanf("%d",&a[i].grow),sumg+=a[i].grow;
if(sumh<=h)
return 0&printf("0\n");
sort(a+1,a+n+1);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=i; j++)
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]+a[i].h+j*a[i].grow);
}
}
for(int i=0; i<=n; i++)
{
int sum=sumg*i+sumh;
if(sum-dp[n][i]<=h)
{
printf("%d\n",i);
return 0;
}
}
printf("%d\n",-1);
return 0;
}