[BZOJ]1226 [SDOI2009]學校食堂Dining

題目地址:[BZOJ]1226 [SDOI2009]學校食堂Dining;


題面:

1226: [SDOI2009]學校食堂Dining

Time Limit: 10 Sec Memory Limit: 259 MB

Description

F 的學校在城市的一個偏僻角落,所有學生都只好在學校吃飯。學校有一個食堂,雖然簡陋,但食堂大廚總能做出讓同學們滿意的菜餚。當然,不同的人口味也不一定相同,但每個人的口味都可以用一個非負整數表示。由於人手不夠,食堂每次只能爲一個人做菜。做每道菜所需的時間是和前一道菜有關的,若前一道菜的對應的口味是a ,這一道爲b ,則做這道菜所需的時間爲(a or b)(a and b) ,而做第一道菜是不需要計算時間的。其中,orand 表示整數逐位或運算及逐位與運算,C語言中對應的運算符爲“|”和“&”。學生數目相對於這個學校還是比較多的,吃飯做菜往往就會花去不少時間。因此,學校食堂偶爾會不按照大家的排隊順序做菜,以縮短總的進餐時間。雖然同學們能夠理解學校食堂的這種做法,不過每個同學還是有一定容忍度的。也就是說,隊伍中的第 i 個同學,最多允許緊跟他身後的 Bi 個人先拿到飯菜。一旦在此之後的任意同學比當前同學先拿到飯,當前同學將會十分憤怒。因此,食堂做菜還得照顧到同學們的情緒。現在,小F 想知道在滿足所有人的容忍度這一前提下,自己的學校食堂做完這些菜最少需要多少時間。

Input

第一行包含一個正整數 C ,表示測試點的數據組數。每組數據的第一行包含一個正整數 N ,表示同學數。每組數據的第二行起共 N 行,每行包含兩個用空格分隔的非負整數 TiBi ,表示按隊伍順序從前往後的每個同學所需的菜的口味和這個同學的忍受度。每組數據之間沒有多餘空行。

Output

包含 C 行,每行一個整數,表示對應數據中食堂完成所有菜所需的最少時間。

Sample Input

2
5
5 2
4 1
12 0
3 3
2 2
2
5 0
4 0

Sample Output

16
1

HINT

對於第一組數據:同學 1 允許同學 2 或同學 3 在他之前拿到菜;同學 2 允許同學 3 在他之前拿到菜;同學 3 比較小氣,他必須比他後面的同學先拿菜…… 一種最優的方案是按同學 3 、同學 2 、同學 1 、同學 4 、同學 5 做菜,每道菜所需的時間分別是 08161
【數據規模和約定】
對於 30 的數據,滿足1N20
對於 100 的數據,滿足1N1,0000Ti1,0000Bi71C5
存在 30 的數據,滿足0Bi1 。存在65%的數據,滿足0Bi5
存在 45 的數據,滿足0Ti130


題解:
首先可以發現(a or b)(a and b)=(a xor b) ,也就是a ^ b.(簡化運算,雖然並沒有什麼用的樣子)
本題應該可以一眼看出是道 DP 題,通過題目中的時間計算公式是與上一個拿菜的人有關,因此數組中的下標需要保存上一個人是誰。又因爲每個人都有個忍耐度,而這個忍耐度非常小,最大隻有 7 ,也就是說到一個人爲止,前面的人都已經打好菜了,這個人與他之後的人打菜的狀況數最多隻有 28 也就是 256 種情況。從這裏就可以發現這是道狀壓 DP
設數組 f[i][j][k] 爲前 i1 個人都已經打好飯了,上次打飯是第 i+k 個人(有可能是之前的人,所以 k 可能是負數,所以還要加上8 ),第 i 個人與後面 7 個人的打飯狀態爲 j (1代表打過飯了)。
當前狀態爲:f[i][j][k]
j & 1 == 1 的時候,說明第 i 個人已經打好飯了,當前狀態其實是和f[i+1][j>>1][k8] 的狀態一樣,就可以直接轉移(繼承)。畢竟前i 個人都已經打好飯了。
j & 1 != 1 的時候。只能向後枚舉下一次打飯的人是誰,當然只能選沒打過飯的,而且在選的過程中要保證在所有人的忍耐度內。就可以轉移了,計算的時候直接用a ^ b就行了。


代碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
#define N 1005
#define INF 0x3f3f3f3f
inline int read(){
    char ch=getchar(); int x=0,f=1;
    while (ch<'0'||ch>'9') { if (ch=='-') f=-1; ch=getchar(); }
    while (ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); }
    return x*f;
}

int n;
int f[N][20][256];
int w[N],b[N];

int main(int argc, char const *argv[]){
    int T = read();
    while(T--){
        memset(f,0x3f,sizeof f);
        n = read();
        for(int i = 1;i <= n;++i){
            w[i] = read(); b[i] = read();
        }
        f[1][7][0] = 0;
        for(int i = 1;i <= n;++i)
            for(int j = 0;j <= (1<<8)-1;++j)
                for(int k = -8;k <= 7;++k)
                    if(f[i][k+8][j] < INF){
                        if(j & 1)
                            f[i+1][k+7][j>>1] = min(f[i+1][k+7][j>>1],f[i][k+8][j]);
                        else{
                            int p = INF;
                            for(int z = 0;z <= 7;++z){
                                if(!((j>>z) & 1)){
                                    if(i+z > p) break;
                                    p = min(p,i+z+b[i+z]);
                                    f[i][z+8][j | (1<<z)] = min(f[i][z+8][j | (1<<z)],f[i][k+8][j]+(i+k?(w[i+k]^w[i+z]):0));
                                }
                            }
                        }
                    }
        int ans = INF;
        for(int i = 0;i <= 8;++i)
            ans = min(ans,f[n+1][i][0]);
        printf("%d\n", ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章