2018.09.22 ZJOI2005午餐(貪心+01揹包)
描述
上午的訓練結束了,THU ACM小組集體去吃午餐,他們一行N人來到了著名的十食堂。這裡有兩個打飯的視窗,每個視窗同一時刻只能給一個人打飯。由於每個人的口味(以及胃口)不同,所以他們要吃的菜各有不同,打飯所要花費的時間是因人而異的。另外每個人吃飯的速度也不盡相同,所以吃飯花費的時間也是可能有所不同的。 THU ACM小組的吃飯計劃是這樣的:先把所有的人分成兩隊,並安排好每隊中各人的排列順序,然後一號隊伍到一號視窗去排隊打飯,二號隊伍到二號視窗去排隊打飯。每個人打完飯後立刻開始吃,所有人都吃完飯後立刻集合去六教地下室進行下午的訓練。 現在給定了每個人的打飯時間和吃飯時間,要求安排一種最佳的分隊和排隊方案使得所有人都吃完飯的時間儘量早。 假設THU ACM小組在時刻0到達十食堂,而且食堂裡面沒有其他吃飯的同學(只有打飯的師傅)。每個人必須而且只能被分在一個隊伍裡。兩個視窗是並行操作互不影響的,而且每個人打飯的時間是和視窗無關的,打完飯之後立刻就開始吃飯,中間沒有延遲。 現在給定N個人各自的打飯時間和吃飯時間,要求輸出最佳方案下所有人吃完飯的時刻。
##輸入
第一行一個整數N,代表總共有N個人。 以下N行,每行兩個整數 Ai,Bi。依次代表第i個人的打飯時間和吃飯時間。
####輸出 一個整數T,代表所有人吃完飯的最早時刻。
####樣例輸入 5 2 2 7 7 1 3 6 4 8 5 ####樣例輸出 17 ####提示 所有輸入資料均為不超過200的正整數。 樣例說明 方案如下: 視窗1: 視窗2: 7 7 1 3 6 4 8 5 2 2 ####標籤 ZJOI2005
一道簡單的線性dp。 直接表示前i個人,第一組打飯一共j分鐘所需要的最少時間。 由於所有人打飯的總時間是相同的。 因此只跟吃飯時間有關係。 於是我們按吃飯時間從大到小排序。 然後就是簡單01揹包分類討論了。 程式碼:
#include<bits/stdc++.h>
#define N 50005
using namespace std;
inline int read(){
int ans=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
return ans;
}
int n,f[2][N],tmp,sum[N];
struct node{int a,b;}peo[N];
inline bool cmp(node a,node b){return a.b>b.b;}
int main(){
n=read();
for(int i=1;i<=n;++i)peo[i].a=read(),peo[i].b=read();
sort(peo+1,peo+n+1,cmp),tmp=0;
for(int i=1;i<=n;++i)sum[i]=sum[i-1]+peo[i].a;
for(int i=1;i<=n;++i){
tmp^=1,memset(f[tmp],0x3f,sizeof(f[tmp]));
for(int j=0;j<=sum[i];++j){
if(j>=peo[i].a)f[tmp][j]=min(f[tmp][j],max(j+peo[i].b,f[tmp^1][j-peo[i].a]));
if(sum[i]-j>=peo[i].a)f[tmp][j]=min(f[tmp][j],max(f[tmp^1][j],sum[i]-j+peo[i].b));
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<=sum[n];++i)ans=min(ans,f[tmp][i]);
cout<<ans;
return 0;
}