这个题的状态比较难想。
首先能想到的是按照吃饭时间倒着排序。如果选中了相同的序列,那么显然吃饭时间越小的排在后面最后的结果越好。
既然是两个队列,想到开一个f(i,j)f(i,j)f(i,j),其中i,ji,ji,j表示每个队列里面选到的最后一个同学在原序列中的index。一开始想f(i,j)=min(f(i−1,j)+X,f(i,j−1)+Y)f(i,j)=min\bigg(f(i-1,j)+X,f(i,j-1)+Y \bigg)f(i,j)=min(f(i−1,j)+X,f(i,j−1)+Y)之类的模型,但是发现你不知道具体的时间,此路不通。想到用时间做状态,但是看起来有点大。
然后看了一下答案,发现是在打饭时间上做0-1背包。O(MN)O(MN)O(MN)是可以过的。
状态f(i,j)f(i,j)f(i,j)表示第一个队列打饭时间到jjj,两个队列已经选完iii个同学时的最小值。
这时隐含着另一个队列的打饭时间k=Si−jk=S_i-jk=Si−j
选第一个或者选第二个,就可以开始搞了
f(i,j)=min(max(f(i,j−a),j+b),max(f(i,j),k+b))f(i,j)=min\bigg(max\big(f(i,j-a),j+b \big),max\big(f(i,j),k+b\big)\bigg)f(i,j)=min(max(f(i,j−a),j+b),max(f(i,j),k+b))
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<ll> vll;
typedef unsigned long long ull;
const int N = 202, M = 40004;
pii x[N];
int s[N];
int n;
int f[2][M];
int main(){
// freopen("in.txt", "r", stdin);
cin >> n;
for(int i = 1; i <= n; ++ i) {
int a, b;
cin >> a >> b;
x[i] = {a, b};
}
sort(x + 1, x + n + 1, [](pii a, pii b) {
if(a.second == b.second)
return a.first < b.first;
return a.second > b.second;
});
for(int i = 1; i <= n; ++ i)
s[i] = s[i - 1] + x[i].first;
memset(f, 0x3f, sizeof(f));
f[0][0] = 0;
int op = 1, q = 1 - op;
for(int i = 1; i <= n; ++ i, op = 1 - op, q = 1 - q) {
memset(f[op], 0x3f, sizeof(f[op]));
auto [a, b] = x[i];
for(int j = 0; j <= s[i]; ++ j) {
int k = s[i] - j;
if(j >= a) {
int t0 = max(f[q][j - a], j + b);
f[op][j] = min(f[op][j], t0);
}
int t0 = max(f[q][j], k + b);
f[op][j] = min(f[op][j], t0);
}
}
int ans = 1 << 30;
for(int j = 0; j <= s[n]; ++ j) {
ans = min(ans, f[q][j]);
}
printf("%d\n", ans);
return 0;
}