1. 程式人生 > >(狀壓dp)P2157 [SDOI2009]學校食堂

(狀壓dp)P2157 [SDOI2009]學校食堂

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;
    }
}