P1156 垃圾陷阱
思路:
很顯然這是一道動態規劃,且每個物品有兩種選擇:用作增加生命或高度。
增加高度會損失一定的生命,反之同理,是不是很像揹包問題?確實是它的變形。
考慮設 \(f[i][j]\) 表示到第 \(i\) 個物品,生命值為 \(j\) 時所到達的最大高度。
那麼我們考慮轉移,顯然對於物品 \(i\) 用作增加高度有:
\(f[i][j]=max(f[i-1][j]+h[i])\)
用作增加生命有:
\(f[i][j]=max(f[i-1][j-f[i]])\)
坑點:
-
注意轉移呼叫變數時奶牛是否還存活。
-
給出的垃圾掉落時間的 \(t\) 陣列不保證單調遞增,需要排序。
程式碼:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct node {
ll t, f, h, sum;
bool operator < (node &a) {return t<a.t;}
}a[1005];
ll h, n;
ll f[105][100005]; //f[i][j]表示前i個垃圾生命值為j時能達到的最高高度
int main() {
cin >> h >> n;
for(int i=1; i<=n; i++) cin >> a[i].t >> a[i].f >> a[i].h; //t表示投放時間,f表示增加的生命值,h表示墊高高度
sort(a+1, a+n+1); //注意給出的時間不遞增
a[0].sum=10;
for(int i=1; i<=n; i++) a[i].sum=a[i-1].sum+a[i].f; //a[i].sum表示到i能達到的最大高度
memset(f, -0x3f, sizeof(f));
f[0][10]=0; //初始化
for(int i=1; i<=n; i++) {
if(a[i].t>a[i-1].sum) { //就算前面的所有垃圾都吃也堅持不到這個垃圾,輸出最大高度
cout << a[i-1].sum;
exit(0);
}
for(int j=a[i].t; j<=a[i].sum; j++) { //j從a[i].t開始列舉保證能存活到當前垃圾投放,且最大高度不大於a[i].sum
f[i][j]=max(f[i][j], f[i-1][j]+a[i].h); //這個垃圾用來墊高高度
//接下來考慮這個垃圾用來增加生命值,那麼j-a[i].f>=0且j-a[i].f>=a[i].t(保證能活到當前垃圾時間)
if(j-a[i].f>=0&&j-a[i].f>=a[i].t) f[i][j]=max(f[i][j], f[i-1][j-a[i].f]);
if(f[i][j]>=h) { //如果不小於h說明到i時就出去了,輸出a[i].t
cout << a[i].t;
exit(0);
}
}
}
cout << a[n].sum; //能執行到這裡說明所有最大高度不足以出洞
return 0;
}