1. 程式人生 > >【BZOJ1226】[SDOI2009] 學校食堂

【BZOJ1226】[SDOI2009] 學校食堂

題目描述

小F 的學校在城市的一個偏僻角落,所有學生都只好在學校吃飯。學校有一個食堂,雖然簡陋,但食堂大廚總能做出讓同學們滿意的菜餚。當然,不同的人口味也不一定相同,但每個人的口味都可以用一個非負整數表示。 由於人手不夠,食堂每次只能為一個人做菜。做每道菜所需的時間是和前一道菜有關的,若前一道菜的對應的口味是a,這一道為b,則做這道菜所需的時間為(a or b)-(a and b),而做第一道菜是不需要計算時間的。其中,or 和and 表示整數逐位或運算及逐位與運算,C語言中對應的運算子為“|”和“&”。

學生數目相對於這個學校還是比較多的,吃飯做菜往往就會花去不少時間。因此,學校食堂偶爾會不按照大家的排隊順序做菜,以縮短總的進餐時間。

雖然同學們能夠理解學校食堂的這種做法,不過每個同學還是有一定容忍度的。也就是說,隊伍中的第i 個同學,最多允許緊跟他身後的Bi 個人先拿到飯菜。一旦在此之後的任意同學比當前同學先拿到飯,當前同學將會十分憤怒。因此,食堂做菜還得照顧到同學們的情緒。 現在,小F 想知道在滿足所有人的容忍度這一前提下,自己的學校食堂做完這些菜最少需要多少時間。

輸入輸出格式

輸入格式:

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

輸出格式:

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

輸入輸出樣例

輸入樣例#1: 
2
5
5 2
4 1
12 0
3 3
2 2
2
5 0
4 0
輸出樣例#1: 
16
1

說明

對於第一組資料:

同學1允許同學2或同學3在他之前拿到菜;同學2允許同學3在他之前拿到菜;同學3比較小氣,他必須比他後面的同學先拿菜……

一種最優的方案是按同學3、同學2、同學1、同學4、同學5做菜,每道菜所需的時間分別是0、8、1、6及1。

【資料規模和約定】

對於30%的資料,滿足1 ≤ N ≤ 20。

對於100%的資料,滿足1 ≤ N ≤ 1,000,0 ≤ Ti ≤ 1,000,0 ≤ Bi ≤ 7,1 ≤ C ≤ 5。

存在30%的資料,滿足0 ≤ Bi ≤ 1。

存在65%的資料,滿足0 ≤ Bi ≤ 5。

存在45%的資料,滿足0 ≤ Ti ≤ 130。


 

 

 

有點難度的狀態壓縮DP呢,一開始真的一直想不到狀態轉移的方法。

(別告訴我你完全沒有想到狀態壓縮,B[i]才7誒!)

一個人既和前面的人有關係,又和後面的人有關係,真是沒法轉移了。。。


但是總歸是有辦法的

 

我們開一個DP陣列

f[i][st][k]

表示此時考慮到第 i 個人,從第i個人開始(包括他自己)後面七個人的吃飯狀態是st(1表示吃了,0表示沒吃),前一個吃飯的人與i的距離為k(因而k的範圍就是 [-8,7]對吧^.^)所用時間的最小值。

所以此時的第i個人不一定吃了哦。

 

思考下面兩種情況:

         1. 此時列舉到的i,已經吃過了(st裡i對應的位置就為1),那麼後面的人不管按什麼順序吃,i都不會生氣。

            所以這時可以直接更新i+1的狀態即 f[i+1][st>>1][k-1]=min(f[i+1][st>>1][k-1],f[i][st][k])

        (向後移一個人,st要右移,前一個吃完的人離i+1的距離變成k-1)

 

         (i同學:“你們愛咋咋,反正我已經吃完了,i+1同學,我可以直接更新你的狀態了。”)

 

         2.此時列舉到的i,還沒有吃飯 ,那麼他最多隻能忍受後面B[i]個人比他先吃到飯,所以這時候就可以列舉從i開始後面的人誰先吃飯了。

            所以此時用lx來列舉後面的人(0<=lx<=7),f[i][ st|tw[lx] ] [lx]=min(f[i][st][k]+Time(i+k,i+lx),f[i][ st|tw[lx] ][lx])

           不過還要注意後面的人的B[i+lx]的值是不一樣的,我們要開一個變數rx來記錄最右能列舉到的lx,並隨時更新rx(不然會有很多人生氣的!),當lx>rx時,    適時地break就可以了。

 

        (i同學:“我還沒吃飯!我後面B[i]個人後的人誰敢先吃飯,我就讓你WA”)

 

最後的答案當然就存在 f[n][1][k]裡啦 (-8<=k<=0)

B[i]最多隻會有7,而n也才1000,複雜度是很優秀的呢。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<algorithm>
 6 
 7 #define For(i,a,b) for(register int i=a;i<=b;++i)
 8 #define Re register
 9 #define inf 0x7f7f7f
10 #define Nm(i) i+8
11 #define Pn putchar('\n')
12 
13 using namespace std;
14 const int N=1e3+10,ST=(1<<8)+10;
15 int f[N][ST][20];
16 int n,m,T[N],B[N],tw[20],nf;
17 
18 inline void read(int &v){
19     v=0;
20     char c=getchar();
21     while(c<'0'||c>'9')c=getchar();
22     while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar();
23 }
24 void write(int x){
25     if(x>9)write(x/10);
26     int xx=x%10;
27     putchar(xx+'0');
28 }
29 int TM(int x,int y){
30     if(x==0)return 0;
31     return T[x]^T[y];
32 }
33 
34 int main(){
35     freopen("dining.in","r",stdin);
36     freopen("dining.out","w",stdout);
37     int Tx; read(Tx);
38     For(i,0,10)tw[i]=1<<i;
39     while(Tx--){
40         read(n);
41         For(i,1,n) read(T[i]),read(B[i]);
42         
43         memset(f,inf,sizeof(f));
44         nf=f[0][0][0];
45         T[0]=B[0]=0;
46         
47         f[1][0][Nm(-1)]=0;
48         For(i,1,n) For(st,0,tw[8]-1) For(k,-8,7){
49             int Fx=f[i][st][Nm(k)];
50             if(Fx==nf)continue;
51             //cout<<Fx<<endl;
52             if(st&1){
53                 f[i+1][st>>1][Nm(k-1)]=min(f[i+1][st>>1][Nm(k-1)],Fx);
54             }else{
55                 int mxR=nf;
56                 For(lx,0,7){
57                     if(st&tw[lx])continue;
58                     if(i+lx>mxR)break;
59                     mxR=min(mxR,i+lx+B[i+lx]);
60                     
61                     f[i][ st|tw[lx] ][ Nm(lx) ] =min( f[i][ st|tw[lx] ][ Nm(lx) ],Fx+TM(i+k,i+lx) );
62                     
63                 }
64             }
65         }
66         int fn=nf;
67         For(i,-8,0)fn=min(fn,f[n][1][Nm(i)]);
68         write(fn); Pn;
69     }
70     fclose(stdin); fclose(stdout);
71     return 0;
72     
73 }

 

求資瓷~