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(i1,j)+X,f(i,j1)+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=Sij
选第一个或者选第二个,就可以开始搞了
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,ja),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; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值