【题解】SDOI2009学校食堂
不知道有没有人跟我有一样的感觉……实际上很多的状压DP都不难,然而调到心碎……这题题面看起来很长,还有混合的‘位运算’来吓唬人(实际上就是异或而已)。但实际上只要仔细阅读,发现也是一道水水的裸题。
首先,题目当中给出的信息是:\(B_{i} <= 7\)。看到这一条,心中已有八分笃定:在这样的环境下,估计是状压。然后就开始考虑转移的方程: 先从暴力的状态开始,我们要确定没有后效性的状态,则有两个维度应该是必须的。一维代表 \(i\),即现在 \(1 -> i\) 之间的同学都已经打到饭了,以及 \(j\) 即上一名打饭的同学的口味值。最后的一维状压,压 \(\left (i + 1, i + 8 \right )\) 号同学的打饭状态,后面的就不用了,因为第 \(i + 1\) 个人目前还没有打到饭,他最大只能容忍第 \(i + 8\) 名同学先打。
可是出现了一个问题:\(j\)的范围过大。所以不能存值,只能存编号。考虑在第\(i + 1\) 个人还没有打饭的情况下,上一个打饭的人只能在范围 \(\left (i - 7, i + 8 \right )\) 中,我们存下这一个编号,并且规定一个标准:第 \(i\) 名同学的编号为 \(7\)。这样,所有可能的同学编号均在 \(\left (0, 15 \right )\) 的范围内。
感觉我的状压dp代码有毒……食用需谨慎呐 ̄へ ̄
#include <bits/stdc++.h> using namespace std; #define maxn 1005 #define INF 9999999 int T, CNST; int n, a[maxn], t[maxn]; int f[maxn][170][505]; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } void Init() { for(int i = 0; i <= n; i ++) for(int j = 0; j <= 15; j ++) for(int k = 0; k < CNST; k ++) f[i][j][k] = INF; } void update(int &x, int y) { x = x < y ? x : y; } int main() { T = read(), CNST = (1 << 8) - 1; while(T --) { n = read(); Init(); for(int i = 1; i <= n; i ++) a[i] = read(), t[i] = read(); f[0][7][0] = 0; for(int i = 0; i < n; i ++) for(int k = 0; k < CNST; k ++) { for(int j = 0; j <= 15; j ++) { if(f[i][j][k] >= INF) continue; int tmp = k, minn = INF; for(int s = 0; s <= 7 && (i + s + 1) <= n; s ++) if(!((tmp >> s) & 1)) minn = min(minn, s + t[i + s + 1]); if(minn == INF || k & 1) minn = 0; for(int s = 1; s <= minn; s ++) { if((k >> s) & 1) continue; if(i + s + 1 > n) break; int q = i + j - 7 >= 0 ? a[i + j - 7] : 0; if(!i && !k) q = a[i + s + 1]; int ret = q ^ a[i + s + 1]; if(s + 8 <= 15) update(f[i][s + 8][k | (1 << s)], f[i][j][k] + ret); } int q = (i + j - 7) >= 0 ? a[i + j - 7] : 0; if(!i && !k) q = a[i + 1]; update(f[i + 1][7][k >> 1], f[i][j][k] + (q ^ a[i + 1])); if(j && (k & 1)) update(f[i + 1][j - 1][k >> 1], f[i][j][k]); } } int ans = INF; for(int j = 0; j <= 15; j ++) for(int k = 0; k < CNST; k ++) ans = min(ans, f[n][j][k]); printf("%d\n", ans); } return 0; }