原題:
CF864E Fire
略
首先, 我們來證明一個東西:我們救東西的順序為按銷燬時間從早到晚, 那一定有一種情況是最優解。
證明:
若存在一個最優解不滿足該證明, 則若我們重新將這幾個東西按照銷燬順序重新排列, 那麼我們只需要證明這個重新排列的順序是可以成立的就行了。
那麼我們來看一下, 設原來的前
i
i
i項和記為
S
i
Si
Si, 第
i
i
i項為
A
i
Ai
Ai, 新的分別為
S
′
i
S'i
S′i和
A
′
i
A'i
A′i。明顯可得
S
n
=
S
′
n
,
A
n
≤
A
′
n
Sn = S'n, An \le A'n
Sn=S′n,An≤A′n, 那麼第n項肯定成立, 而原序列去掉新序列中的第n項一定成立(這很顯然嘛),那麼若長度為
n
−
1
n - 1
n−1的序列變化成立, 那長度為
n
n
n的序列也成立。 而最後只有一項時是明顯成立,所以證畢。
接下來我們回到原來的題目。首先我們定義狀態為
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j], 表示前
i
i
i個元素在第
j
j
j時刻前救出的最大價值, 而第
i
i
i個元素的銷燬時間我們知道, 所以我們就只能將這個元素放在銷燬時間之前救出, 然後就如同
0
/
1
0/1
0/1揹包一樣救援時間當成體積, 價值當成價值。最後有一點要注意, 因為有銷燬時間的存在, 所以我們的答案是在陣列裡最大的那個。
程式碼:
#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 2000
int st[MAXN + 5], sd[MAXN + 5], sp[MAXN + 5];
pair <int, int> s[MAXN + 5];
int dp[MAXN + 5];
int answer [MAXN + 5];
bool f[MAXN + 5][MAXN + 5];
int sa[MAXN + 5];
int main () {
int n;
int sum = 0;
scanf ("%d", &n);
for (int i = 1; i <= n; i ++) {
scanf ("%d %d %d", &st[i], &sd[i], &sp[i]);
s[i].first = sd[i];
s[i].second = i;
sum = max (sum, sd[i]);
}
sort (s + 1, s + 1 + n);
for (int i = 1; i <= n; i ++) {
for (int j = s[i].first - 1; j >= st[s[i].second]; j --) {
if (dp[j] < dp[j - st[s[i].second]] + sp[s[i].second]) {
dp[j] = dp[j - st[s[i].second]] + sp[s[i].second];
f[i][j] = 1;
answer[j] = answer[j - st[s[i].second]] + 1;
}
}
}
int smax = 0, maxl = 0;
for (int i = 1; i <= sum; i ++) {
if (dp[i] > smax) {
smax = dp[i];
maxl = i;
}
}
printf ("%d\n%d\n", smax, answer[maxl]);
int tot = 0;
for (int i = n, j = maxl; i > 0; i --) {
if (f[i][j]) {
sa[++tot] = i;
j -= st[s[i].second];
}
}
for (int i = tot; i >= 1; i --) {
printf ("%d ", s[sa[i]].second);
}
}