第三题 草草草(grass)
问题描述:
有N棵小草,编号0至N-1。乐滋滋巨神不喜欢小草,所以乐滋滋要剪草,使得N棵小草的高度总和不超过H。在第0时刻,第i棵小草的高度是h[i],接下来的每个整数时刻,会依次发生如下三个步骤:
(1)每棵小草都长高了,第i棵小草长高的高度是grow[i]。
(2)乐滋滋选择其中一棵小草并把它剪平,这棵小草高度变为0。注意:这棵小草并没有死掉,它下一秒还会生长的。
(3)乐滋滋计算一下这N棵小草的高度总和,如果不超过H,则完成任务,一切结束, 否则轮到下一时刻。
你的任务是计算:最早是第几时刻,乐滋滋能完成他的任务?如果第0时刻就可以完成就输出0,如果永远不可能完成,输出-1,否则输出一个最早的完成时刻。
输入格式:
第一行,两个整数N和H。 1 ≤ N ≤ 50,0 ≤ H ≤ 1000000。
第二行,N个整数,表示h[i]。0 ≤ h[i] ≤ 100000。
第三行,N个整数,表示grow[i]。1 ≤ grow[i] ≤ 100000。
对于20%的数据, 1 ≤ N
≤ 7。
输出格式:
一个整数,最早完成时刻或-1。
输入输出样例:
输入样例 |
3 16 5 8 58 2 1 1 |
2 58 5 8 2 1
|
2 0 5 8 2 1
|
7 33 5 1 6 5 8 4 7 2 1 1 1 4 3 2 |
输出样例 |
1 |
0 |
-1 |
5 |
样例解释 |
到了第1时刻,三棵小草的高度依次是7,9,59。如果乐滋滋把高度是59的小草剪掉,那么三棵小草高度是7+9+0=16,任务完成。 |
|
|
第1秒剪第2棵小草,第2秒剪第0棵小草,第3秒剪6棵小草,第4秒剪第5棵小草,第5秒剪第4棵小草。 |
本体是背包+贪心
由于值会变,需要对物品选取的先后顺序排序
毫无疑问 一棵草不可能✄两次 否则无解
另外,如果最后砍得草是已知的(对应题中N=最早时刻),那这题肯定是最后砍长得快的
进而衍生,倘若最后砍的草是确定的,先砍长得慢的
我们用w(i) 表示第i时刻的价值,显然不砍的话,它是单调递增的(grow[i]>0)
所以我们在砍长得第i慢的草时,只有长速为1~i-1 的草有可能被砍,后面的草一定不会砍的
所以它的状态可以由前(i-1)棵草 得到,有单调子结构,可以Dp背包
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<cctype> #include<iostream> #include<functional> #include<algorithm> using namespace std; #define MAXH (1000000+10) #define MAXN (50+10) struct grass { int h,g; friend bool operator<(const grass a,const grass b){return a.g<b.g;} //先割长得慢的,后割快的 int w(const int i){return h+i*g;} }a[MAXN]; int n,H,f[MAXN][MAXN]; //f[i][j]表示 到第i棵 且 取了 j 棵 bool is_ok(int m) { memset(f,0,sizeof(f)); for (int i=1;i<=n;i++) for (int j=1;j<=min(i,m);j++) { f[i][j]=max(f[i-1][j],f[i-1][j-1]+a[i].w(j)); //如果到第i次时割j稞最优值 就是0/1背包 } int ans=0; for (int i=1;i<=n;i++) ans+=a[i].w(m); if (ans-f[n][m]<=H) { cout<<m<<endl; // while (1); exit(0); } return 0; } int main() { freopen("grass.in","r",stdin); freopen("grass.out","w",stdout); scanf("%d%d",&n,&H); for (int i=1;i<=n;i++) cin>>a[i].h; for (int i=1;i<=n;i++) cin>>a[i].g; sort(a+1,a+1+n); for (int i=0;i<=n;i++) is_ok(i); cout<<"-1n"; // while (1); return 0; }