P2577 [ZJOI2005]午餐 - 贪心 - dp

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Fantasy_World/article/details/82935385

首先考虑只放一队的情况,显然是吃饭时间长的优先打饭,然而我没这么想直接套了国王游戏的模型,事实上还是从别的角度多想想比较好
然后是dp,这道题怎么安排人到不同的窗口很难说,所以考虑用最暴力的状态和转移(反正数据范围小)
设f[i][j][k]表示安排了前i人,第一个窗口的打饭总时间为j,第二个窗口打饭总时间为k,最优集合时间是多少
然后决策一下每个人在哪打饭,并且注意一下答案的更新
但是会爆内存,考虑到j和k其实是唯一对应的,可以省去一维,设sum[i]为前i个人打饭时间之和,那么k = sum[i] - j,因此可以直接算出k,没必要多开一维
转移时注意前面的人吃饭时间可能比一个之后打饭的人吃完还慢

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 200 + 10;
struct lunch{
	int a, b;
}lun[MAXN];
int n,ans=1<<30,f[MAXN][40000 + 10],sum[MAXN];
bool cmp(lunch a, lunch b) {
	//return a.b > b.b; 
	int ai = a.a, bi = a.b, aj = b.a, bj = b.b;
	return max(ai+bi, ai+aj+bj) < max(aj+bj, aj+ai+bi);
} 
int main() {
	scanf("%d", &n);
	for(int i=1; i<=n; i++) {
		scanf("%d%d", &lun[i].a, &lun[i].b);
	}
	sort(lun+1, lun+n+1, cmp);
	for(int i=1; i<=n; i++) {
		sum[i] = sum[i-1] + lun[i].a;
	}
	memset(f, 0x3f, sizeof(f));
	f[0][0] = 0;
	for(int i=1; i<=n; i++) {
		for(int j=0; j<=sum[i]; j++) {
			if(j >= lun[i].a) f[i][j] = min(f[i][j], max(f[i-1][j-lun[i].a], j+lun[i].b));
			f[i][j] = min(f[i][j], max(f[i-1][j], sum[i]-j+lun[i].b));
		}
	}
	for(int i=0; i<=sum[n]; i++) {
		ans = min(ans, f[n][i]);
	}
	printf("%d", ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Fantasy_World/article/details/82935385