洛谷P3886 [JLOI2009]神祕的生物(插頭dp)
阿新 • • 發佈:2020-07-21
洛谷P3886 [JLOI2009]神祕的生物(插頭dp)
題目大意
求帶權正方形棋盤中最大權聯通塊
資料範圍
\[1 \le n \le 9 \]
解題思路
自己做出來的第一道插頭 dp,不過很簡單就是了
由於這道題維護的是連通性,所以要用最小表示法記錄一下每個點所在的聯通塊編號,然後就是輪廓線保留 n 個格子即可,因為插頭處只用看看左邊有沒有塊
那麼就分為兩種情況了,一種是不放插頭,一種是放一個插頭
不放插頭要看看是不是會導致圖不連通,放插頭要將左和上的兩個格子的聯通塊合併
程式碼
const int N = 5050; const int P = 3592; int h[N], ne[N], st[2][N], tot[2], val[2][N], nw, pr; void insert(int bit, int num) { // printf ("%d %d\n", bit, num); int x = bit % P + 1; for (int i = h[x]; i; i = ne[i]) if (st[nw][i] == bit) return Mx(val[nw][i], num); ne[++tot[nw]] = h[x], st[nw][h[x] = tot[nw]] = bit, val[nw][tot[nw]] = num; } int vis[9], ans = -1e6, n; int reb(int bit) { int now = 0, mx = 0; memset(vis, 0, sizeof(vis)); for (int i = 0;i < n; i++) { int p = bit >> (i * 3) & 7; if (!p) continue; if (!vis[p]) vis[p] = ++mx; now |= vis[p] << (i * 3); } return now; } void update(int now, int val) { for (int l = 0;l < n; l++) if ((now >> (l * 3) & 7) > 1) return; Mx(ans, val); } int a[50][50]; int main() { read(n); for (int i = 1;i <= n; i++) for (int j = 1;j <= n; j++) read(a[i][j]), Mx(ans, a[i][j]); if (ans <= 0) return write(ans), 0; insert(0, 0); for (int i = 1;i <= n; i++) { for (int j = 1;j <= n; j++) { pr = nw, nw = nw ^ 1, tot[nw] = 0; memset(h, 0, sizeof(h)); for (int k = 1;k <= tot[pr]; k++) { int now = st[pr][k], V = val[pr][k]; int b1 = (j == 1) ? 0 : now >> ((j - 2) * 3) & 7; int b2 = now >> ((j - 1) * 3) & 7; update(now, V); int cc = 0; for (int l = 0;l < n; l++) { int pp = now >> (l * 3) & 7; if (pp == b2) cc++; } if (cc > 1 || b2 == 0) insert(now ^ (b2 << ((j-1) * 3)), V); for (int l = 0;l < n; l++) { int pp = now >> (l * 3) & 7; if (pp == 0) continue; if (pp == b1 || pp == b2) now ^= (7 ^ pp) << (l * 3); } if (b2 == 0) now |= 7 << ((j-1) * 3); insert(reb(now), V + a[i][j]); } } } for (int i = 1;i <= tot[nw]; i++) update(st[nw][i], val[nw][i]); write(ans); return 0; }