1. 程式人生 > >[洛谷P2577] [ZJOI2005]午餐

[洛谷P2577] [ZJOI2005]午餐

的人 然而 同學 如果 輸入 lin struct 進行 以及

洛谷題目鏈接:[ZJOI2005]午餐

題目描述

上午的訓練結束了,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的正整數。

題意:\(n\)個人.,每個人有一個排隊時間和吃飯時間,兩個窗口,窗口每時每刻都可以有一個人在排隊,且一個人排完隊後另一個人可以馬上接著他排隊,要使每個人都打完飯吃完最短需要多久.

題解: 首先我們先想一下如果只有一個窗口該如何記錄最短時間.如果只有一個窗口,那麽最短時間就只與排隊順序有關了,那麽應該讓誰排在前面呢?我們這裏假設\(i\)的等待時間為\(wait[i]\),吃飯時間為\(eat[i]\),假設要讓\(i\)排在\(j\)前面,就要滿足:\(wait[i]+wait[j]+eat[j] <= wait[j]+wait[i]+eat[i])\)


化一下式子,可以得到\(eat[i] >= eat[j]\),也就是說,按照吃東西的時間從大到小排序,這樣可以得到的結果是最優的.

那麽如果有兩個窗口呢?這裏我們可以用DP的方法來維護一下.我們用狀態\(f[i][j][k]\)表示前\(i\)個人都吃完飯,且在第一個窗口等待時間為\(j\),第二個為\(k\)所需要的最少時間.那麽可以寫出狀態轉移方程:\[f[i][j][k]=min(f[i][j][k], max(f[i-1][j-wait[i]][k], j+eat[i]))\]\[f[i][j][k]=min(f[i][j][k], max(f[i-1][j][k-wait[i]], k+eat[i]))\].

然而這樣做的話時空復雜度是\(O(n*V^2)\)的,其中\(V=n^2\),顯然這樣的復雜度是過不了的.

那麽我們需要對這個狀態進行一些優化.

我們會發現前\(i\)個人的等待時間之和是可以確定的,也就是說,我們並不需要記錄兩個窗口的等待時間,只需要記錄一個窗口的等待時間就可以算出另一個窗口的等待時間,可以設狀態\(f[i][j]\)表示前\(i\)個人都吃完飯,並且第一個窗口等待時間為\(j\).這樣定義的狀態可以保證是唯一的.轉移也類似上面的轉移方法:\[f[i][j] =min(f[i][j], max(f[i-1][j-wait[i]], j+eat[i]))\]\[f[i][j] = min(f[i][j], max(f[i-1][j], sum[i]-j+eat[i]))\]其中\(sum[i]\)表示前\(i\)個人的等待總時間.

#include<bits/stdc++.h>
using namespace std;
const int N = 200+5;
typedef int _int;
#define int long long

int n, sum[N], ans = 2e9;
int f[N][N*N];

struct people{
    int wait, eat;
}a[N];

bool cmpp(people a, people b){ return a.eat > b.eat; }

_int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i].wait >> a[i].eat;
    sort(a+1, a+n+1, cmpp);
    for(int i = 1; i <= n; i++) sum[i] = sum[i-1]+a[i].wait;
    memset(f, 127/3, sizeof(f)), f[0][0] = 0;
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= sum[n]; j++){
            if(j >= a[i].wait) f[i][j] = min(f[i][j], max(f[i-1][j-a[i].wait], j+a[i].eat));
            f[i][j] = min(f[i][j], max(f[i-1][j], sum[i]-j+a[i].eat));
        }
    for(int i = 0; i <= sum[n]; i++) ans = min(ans, f[n][i]);
    cout << ans << endl;
    return 0;
}

[洛谷P2577] [ZJOI2005]午餐