(狀壓dp)P2157 [SDOI2009]學校食堂
阿新 • • 發佈:2018-11-28
https://www.luogu.org/problemnew/show/P2157
小F 的學校在城市的一個偏僻角落,所有學生都只好在學校吃飯。學校有一個食堂,雖然簡陋,但食堂大廚總能做出讓同學們滿意的菜餚。當然,不同的人口味也不一定相同,但每個人的口味都可以用一個非負整數表示。 由於人手不夠,食堂每次只能為一個人做菜。做每道菜所需的時間是和前一道菜有關的,若前一道菜的對應的口味是a,這一道為b,則做這道菜所需的時間為(a or b)-(a and b),而做第一道菜是不需要計算時間的。其中,or 和and 表示整數逐位或運算及逐位與運算,C語言中對應的運算子為“|”和“&”。
學生數目相對於這個學校還是比較多的,吃飯做菜往往就會花去不少時間。因此,學校食堂偶爾會不按照大家的排隊順序做菜,以縮短總的進餐時間。
雖然同學們能夠理解學校食堂的這種做法,不過每個同學還是有一定容忍度的。也就是說,隊伍中的第i 個同學,最多允許緊跟他身後的Bi 個人先拿到飯菜。一旦在此之後的任意同學比當前同學先拿到飯,當前同學將會十分憤怒。因此,食堂做菜還得照顧到同學們的情緒。 現在,小F 想知道在滿足所有人的容忍度這一前提下,自己的學校食堂做完這些菜最少需要多少時間。
2
in:
5
5 2
4 1
12 0
3 3
2 2
out:
16
in:
2
5 0
4 0
out:
1
知道是dp題,然後剛開始只用了二維去寫,第一位存第幾個打飯,第二位存不同的人,結果他轉移的時候出現了重複的人吃飯,不知道怎麼處理
去看題解,發現需要三維,一個人可以由前面和後面的人轉移過來,而且題目給的每個人說能容忍的人只有最多8個,就考慮狀壓。
#include<bits/stdc++.h> #define f(i, j, k) dp[i][j][k + 8] using namespace std; const int maxn = 1000 + 5; int dp[maxn][1 << 8][20]; struct node { int t, b; } a[maxn]; int main() { int t, n, ti, bi; cin >> t; while(t--) { cin >> n; for(int i = 1; i <= n; i++) cin >> a[i].t >> a[i].b; memset(dp, 0x3f3f3f3f, sizeof(dp)); f(1, 0, -1) = 0; for(int i = 1; i <= n; i++) { for(int j = 0; j < (1 << 8); j++) { for(int k = -8; k <= 7; k++) { if(f(i, j, k) == dp[0][0][0]) continue; if(j & 1) //如果i吃飯,狀態dp[i + 1][j >> 1][k - 1]與dp[i + 1][j >> 1][k - 1]等價 f(i + 1, j >> 1, k - 1) = min(f(i + 1, j >> 1, k - 1), f(i, j, k)); else { //i沒有吃,轉移到i-i+7吃飯的狀態 int r = dp[0][0][0]; for(int l = 0; l <= 7; l++) { if((j & (1 << l)) == 0) { if(i + l > r) break; //判斷第i+l打飯是否能被前面的所有人忍受 r = min(r, i + l + a[i + l].b); //j | (1 << l) 讓第i+l個人變為吃飯狀態 f(i, j | (1 << l), l) = min(f(i, j | (1 << l), l), f(i, j, k) + ((i + k) == 0 ? 0 : (a[i + k].t ^ a[i + l].t))); } } } } } } int ans = dp[0][0][0]; for(int k = -8; k <= 0; k++) ans = min(ans, f(n + 1, 0, k)); cout << ans << endl; } }