1. 程式人生 > >【題解】SDOI2009學校食堂

【題解】SDOI2009學校食堂

之間 信息 etc 可能 tin pda 沒有 define 然而

  不知道有沒有人跟我有一樣的感覺……實際上很多的狀壓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; }

【題解】SDOI2009學校食堂