BZOJ1899: [Zjoi2004]Lunch 午餐
Description
上午的训练结束了,THU ACM小组集体去吃午餐,他们一行N人来到了著名的十食堂。
这里有两个打饭的窗口,每个窗口同一时刻只能给一个人打饭。
由于每个人的口味(以及胃口)不同,所以他们要吃的菜各有不同,打饭所要花费的时间是因人而异的。
另外每个人吃饭的速度也不尽相同,所以吃饭花费的时间也是可能有所不同的。
THU ACM小组的吃饭计划是这样的:先把所有的人分成两队,并安排好每队中各人的排列顺序,然后一号队伍到一号窗口去排队打饭,二号队伍到二号窗口去排队打饭。
每个人打完饭后立刻开始吃,所有人都吃完饭后立刻集合去六教地下室进行下午的训练。
现在给定了每个人的打饭时间和吃饭时间,要求安排一种最佳的分队和排队方案使得所有人都吃完饭的时间尽量早。
假设THU ACM小组在时刻0到达十食堂,而且食堂里面没有其他吃饭的同学(只有打饭的师傅)。
每个人必须而且只能被分在一个队伍里。两个窗口是并行操作互不影响的,而且每个人打饭的时间是和窗口无关的,打完饭之后立刻就开始吃饭,中间没有延迟。
现在给定N个人各自的打饭时间和吃饭时间,要求输出最佳方案下所有人吃完饭的时刻。
Input
第一行一个整数N,代表总共有N个人。 以下N行,每行两个整数 Ai,Bi。依次代表第i个人的打饭时间和吃饭时间。
Output
一个整数T,代表所有人吃完饭的最早时刻。
Sample Input
5
2 2
7 7
1 3
6 4
8 5
2 2
7 7
1 3
6 4
8 5
Sample Output
17
HINT
方案如下:
窗口1: 窗口2:
7 7 1 3
6 4 8 5
2 2
【限制】
所有输入数据均为不超过200的正整数。
题解Here!
比较难想的$DP$题。。。
鬼知道要先贪心再$DP$啊。。。
首先贪心:吃饭慢的先打饭可以节约时间, 所以先将人按吃饭时间从大到小排序。
首先,应该想到$dp[i][j][k]$表示前$i$个人,在1号窗口打饭总时间$j$,在2号窗口打饭总时间$k$。
当然这样会爆空间,所以想到去掉一维。
$dp[i][j]$表示前$i$个人,在1号窗口打饭总时间$j$,最早吃完饭的时间
我们可以发现$j+k=\text{前i个人打饭总和}$,则有$k=sum(i)-j$。 所以可以维护一个前缀和$sum[i]$。
接下来是分类讨论$+DP$:
1. 将第$i$个人放在1号窗口:
$$dp[i][j]=\min\{dp[i][j],\max\{dp[i-1][j-cost[i]],j+eat[i]\}\quad|\quad j>=cost[i]\}$$
$dp[i-1][j-cost[i]]$是$i$号人打饭+吃饭的时间不足$i-1$号人吃饭的时间, 所以没有影响。
$j+eat[i]$就是造成了影响。
2. 将第$i$个人放在2号窗口:同理
$$dp[i][j]=\min\{dp[i][j],\max\{dp[i-1][j],sum[i]-j+eat[i]\}\quad|\quad k=sum[i]-j\}$$
然后就没了。
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define MAXN 210 using namespace std; int n,ans=2147483646; int sum[MAXN],dp[MAXN][MAXN*MAXN]; struct Student{ int cost,eat; friend bool operator <(const Student &p,const Student &q){ return p.eat>q.eat; } }a[MAXN]; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } void work(){ dp[0][0]=0; for(int i=1;i<=n;i++) for(int j=0;j<=sum[i];j++){ if(j>=a[i].cost)dp[i][j]=min(dp[i][j],max(dp[i-1][j-a[i].cost],j+a[i].eat)); dp[i][j]=min(dp[i][j],max(dp[i-1][j],sum[i]-j+a[i].eat)); } for(int i=0;i<=sum[n];i++)ans=min(ans,dp[n][i]); printf("%d\n",ans); } void init(){ n=read(); memset(dp,127,sizeof(dp)); for(int i=1;i<=n;i++){a[i].cost=read();a[i].eat=read();} sort(a+1,a+n+1); sum[0]=0; for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i].cost; } int main(){ init(); work(); return 0; }