P2577 [ZJOI2005]午餐 - 貪心 - dp

首先考慮只放一隊的情況,顯然是吃飯時間長的優先打飯,然而我沒這麼想直接套了國王遊戲的模型,事實上還是從別的角度多想想比較好
然後是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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章