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