[ZJOI2005]午餐

題目描述

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

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

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

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

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

輸入格式

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

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

輸出格式

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

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


容易想到的一個貪心策略就是:讓打飯慢的人先打飯。所以我們一開始可以對所有人按照打飯時間排序。

然後我們開始考慮窗口的選擇。顯然,前i個人的最優解一定是由前i-1個人的最優解更新而來的,所以我們可以考慮用動態規劃。考慮第i個人怎麼選窗口會最優。

第i個人選窗口會影響到的因素就是:在1窗口打飯一共用了多少時間、在2窗口打飯一共用了多少時間。那麼我們把這兩個量設計進狀態即可。設dp[i][j][k]表示前i個人在1窗口用了j個單位時間、在2窗口用了k個單位時間的情況下,前i個人吃完飯的最早時刻。

那麼轉移很好想,O(1)轉移即可。但是進一步分析會發現一個問題:j和k的上限爲\(200*200=40000\),這樣開數組肯定會爆的...那麼想一想優化。

可以發現j和k加起來剛好等於前i個人的打飯時間和。那麼換個思路,我們求出前i個人的打飯時間和sum[i],那麼我們只需要知道j就可以求出k了。那麼我們的狀態就可以省掉一維:dp[i][j]表示前i個人在1號窗口用了j個單位時間的情況下,前i個人吃完飯的最早時刻。可以得出轉移方程:

\[ dp[i][j]= \left\{ \begin{array}{rcl} min(max(dp[i-1][j-p[i].a],j+p[i].b),max(dp[i-1][j],k+p[i].b)) & j,k>=a[i]\\ max(dp[i-1][j-p[i].a],j+p[i].b) & j>=a[i]\\ max(dp[i-1][j],k+p[i].b) & k>=a[i] \end{array}\right. \]

初始化\(dp[i][j]=inf\)\(dp[0][0]=0\)。答案就是\(Max{\{}dp[n][i]{\}}\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 201
#define maxm 40001
#define inf 0x3f3f3f3f
using namespace std;

inline int read(){
    register int x(0),f(1); register char c(getchar());
    while(c<'0'||'9'<c){ if(c=='-') f=-1; c=getchar(); }
    while('0'<=c&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}

struct node{ int a,b; }p[maxn];
int dp[maxn][maxm];
int n,c[maxn];

inline bool cmp(const node &x,const node &y){ return x.b>y.b; }
int main(){
    n=read();
    for(register int i=1;i<=n;i++) p[i].a=read(),p[i].b=read();
    
    sort(p+1,p+1+n,cmp);
    for(register int i=1;i<=n;i++) c[i]=c[i-1]+p[i].a;

    memset(dp,0x3f,sizeof dp),dp[0][0]=0;
    for(register int i=1;i<=n;i++){
        for(register int j=0;j<=c[i];j++){
            register int k=c[i]-j;
            if(j>=p[i].a && k>=p[i].a) dp[i][j]=min( max( dp[i-1][j-p[i].a],j+p[i].b ),max( dp[i-1][j],k+p[i].b ) );
            else if(j>=p[i].a) dp[i][j]=max( dp[i-1][j-p[i].a],j+p[i].b );
            else if(k>=p[i].a) dp[i][j]=max( dp[i-1][j],k+p[i].b );
        }
    }

    int ans=inf;
    for(register int i=0;i<=c[n];i++) ans=min( ans,dp[n][i] );
    printf("%d\n",ans);
    return 0;
}

時間複雜度爲\(O(N*40000)\)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章