1. 程式人生 > >[ZJOI2005]午餐 (貪心,動態規劃)

[ZJOI2005]午餐 (貪心,動態規劃)

輸出格式 定義 兩個 其中 AS display 著名 師傅 turn

題目描述

上午的訓練結束了,THU ACM小組集體去吃午餐,他們一行N人來到了著名的十食堂。這裏有兩個打飯的窗口,每個窗口同一時刻只能給一個人打飯。由於每個人的口味(以及胃口)不同,所以他們要吃的菜各有不同,打飯所要花費的時間是因人而異的。另外每個人吃飯的速度也不盡相同,所以吃飯花費的時間也是可能有所不同的。

THU ACM小組的吃飯計劃是這樣的:先把所有的人分成兩隊,並安排好每隊中各人的排列順序,然後一號隊伍到一號窗口去排隊打飯,二號隊伍到二號窗口去排隊打飯。每個人打完飯後立刻開始吃,所有人都吃完飯後立刻集合去六教地下室進行下午的訓練。

現在給定了每個人的打飯時間和吃飯時間,要求安排一種最佳的分隊和排隊方案使得所有人都吃完飯的時間盡量早。

假設THU ACM小組在時刻0到達十食堂,而且食堂裏面沒有其他吃飯的同學(只有打飯的師傅)。每個人必須而且只能被分在一個隊伍裏。兩個窗口是並行操作互不影響的,而且每個人打飯的時間是和窗口無關的,打完飯之後立刻就開始吃飯,中間沒有延遲。

現在給定N個人各自的打飯時間和吃飯時間,要求輸出最佳方案下所有人吃完飯的時刻。

輸入輸出格式

輸入格式:

第一行一個整數N,代表總共有N個人。

以下N行,每行兩個整數 Ai,Bi。依次代表第i個人的打飯時間和吃飯時間。

輸出格式:

一個整數T,代表所有人吃完飯的最早時刻。

輸入輸出樣例

輸入樣例#1:

5
2 2
7 7
1 3
6 4
8 5

輸出樣例#1:

17

說明

所有輸入數據均為不超過200的正整數。


Solution

考慮貪心.


給出貪心條件證明:
令當前,有兩個人分別為 a,b,且滿足 a 在 b 前為更優解.
排隊和吃飯時間分別為:
\[d_a,c_a,d_b,c_b\]
那麽當前如果 a 在 b前,所需要花費的時間即為:
\[d_a+max(c_a,d_b+c_b)\]

同理,如果 b 在 a 前,所需花費的時間為:
\[d_b+max(c_b,d_a+c_a)\]

因為滿足 a 在 b 前條件更優,即滿足關系:
\[d_a+max(c_a,d_b+c_b)<d_b+max(c_b,d_a+c_a)\]


以上貪心是一列隊的做法,對於兩列,考慮DP.

定義狀態:
\[f[i][j]\]
表示到了第 i 個人,第1隊打飯時間 (不包括吃飯)為 j 時的最小集合時間.

轉移方程
對於第 i 個人,它有兩種情況.
1) 去第一隊
\[f[i+1][j+a[i+1].w]=min(f[i+1][j+a[i+1].d],max(j+a[i+1].d+a[i+1].c,f[i][j]));\]
2) 去第二隊
\[f[i+1][j]=min(f[i+1][j],max(f[i][j],a[i+1].c+sum[i]-j+a[i+1].d));\]

其中 sum 代表排序之後的排隊前綴和.

#include<bits/stdc++.h>
using namespace std;
const int maxn=208;
struct sj{
    int c;
    int d;
}a[maxn];
bool cmp(sj s,sj j)
{return s.d+max(s.c,j.c+j.d)<j.d+max(j.c,s.c+s.d);}

int n,sum[maxn];
int f[maxn][maxn*maxn];
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
    cin>>a[i].d>>a[i].c;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++)
    sum[i]=sum[i-1]+a[i].d;
    memset(f,127,sizeof(f));
    int inf=f[0][0];
    f[0][0]=0;
    for(int i=0;i<n;i++) 
    for(int j=0;j<=sum[i];j++) 
    {
        if(f[i][j]==inf)
        continue;
        f[i+1][j+a[i+1].d]=min(f[i+1][j+a[i+1].d],max(j+a[i+1].d+a[i+1].c,f[i][j]));
        f[i+1][j]=min(f[i+1][j],max(f[i][j],a[i+1].c+sum[i]-j+a[i+1].d));
    }
    int ans=19260817;
    for(int i=0;i<=sum[n];i++)
    ans=min(ans,f[n][i]);
    cout<<ans<<endl; 
    return 0;
}

[ZJOI2005]午餐 (貪心,動態規劃)