(状压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;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_40588429/article/details/84500552