Luogu P2577 午餐(ZJOI2004)
这个题的状态比较难想。
首先能想到的是按照吃饭时间倒着排序。如果选中了相同的序列,那么显然吃饭时间越小的排在后面最后的结果越好。
既然是两个队列,想到开一个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;
}