$n leq 50$
sol:
放一个在 $x$ 处拐弯的 $L$ 形石头相当于在水平和垂直方向上各选一个与 $x$ 相邻的点,全局不能重复选
最小化危险度,相当于满足这些限制的情况下石头盖住的点危险度越大越好,而石头有各种各样的限制,考虑费用流
这是一个“只能增广 m 次的最大费用可行流”问题,我们增广到 m 次或者找出来的最长路为负即可
为了满足“不重复选”,可以拆点,每个入点向出点连流量 1,费用为危险度的边
因为每个危险点左右和上下各能选一个,相当于“每头牛都要分到一瓶可乐和一份午饭”(忘了这道题题号了...),可以把每个不危险的点分成“可乐”和“午饭”两类
因为选的两个不危险的点行数奇偶性不同,所以可以考虑按行数奇偶把非危险点分成两类
$S space ightarrow space 每个行数为奇数的非危险点 space ightarrow space 相邻的入点$
$相邻的出点 space ightarrow space 每个行数为偶数的非危险点 space ightarrow space T$
以上两类边流量 1,费用 0
然后愉快的流
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 100010,maxm = 300010,oo = 2147483647; struct ZKW { int head[maxn], nx[maxn], inq[maxn], vis[maxn], dis[maxn]; int n, m, s, t, ans, cost; queue<int> q; struct Edge { int from, to, caps, cost; Edge(){} Edge(int _1, int _2, int _3, int _4) : from(_1), to(_2), caps(_3), cost(_4){} }es[maxm]; ZKW(){memset(head, -1, sizeof(head));} void setn(int _){n = _;} void AddEdge(int u, int v, int w, int c) { es[m] = Edge(u, v, w, c); nx[m] = head[u]; head[u] = m++; es[m] = Edge(v, u, 0, -c); nx[m] = head[v]; head[v] = m++; } bool BFS() { for(int i = 0;i <= n;i++)dis[i] = -oo; dis[t] = 0;inq[t] = 1;q.push(t); while(!q.empty()) { int now = q.front();q.pop(); for(int i = head[now]; i != -1; i = nx[i]) { Edge& e = es[i^1]; if(e.caps && dis[e.from] < dis[now] + e.cost) { dis[e.from] = dis[now] + e.cost; if(!inq[e.from]) { inq[e.from] = 1; q.push(e.from); } } } inq[now] = 0; } if(dis[s] > 0){cost = dis[s];return 1;} return 0; } int DFS(int u, int a) { if(u == t || !a)return ans += cost * a, a; if(vis[u])return 0; vis[u] = 1; int flow = 0, f; for(int i = head[u]; i != -1; i = nx[i]) { Edge& e = es[i]; if(dis[e.to] == dis[u] - e.cost && (f = DFS(e.to, min(e.caps, a)))) { e.caps -= f; es[i^1].caps += f; a -= f; flow += f; if(!a)return flow; } } return flow; } int MaxCostFlow(int _s, int _t, int tms) { s = _s, t = _t; int flow = 0, f; for(int i = 1; i <= tms; i++) if(BFS()) {memset(vis, 0, sizeof(vis)); flow += DFS(s, oo);} return flow; } } sol; int n, m, k, s, t, ans; int mat[160][60],mem[160][160][5],dfn,b[maxn]; inline int pos(int x, int y, int type) { return n * n * (type - 1) + (x - 1) * n + y; } int main() { n = read(), m = read(), k = read(); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) mat[i][j] = read(); s = 0, t = n * n * 2 + 1; sol.setn(t + 1); for(int i = 1; i <= k; i++) { int x = read(), y = read(); b[pos(x, y, 1)] = 1; } for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { ans += mat[i][j]; if(b[pos(i, j, 1)])continue; if((i + j) & 1) sol.AddEdge(pos(i, j, 1), pos(i, j, 2), 1, mat[i][j]); else { if(i & 1) { sol.AddEdge(s, pos(i, j, 1), 1, 0); if(i > 1) sol.AddEdge(pos(i, j, 1), pos(i - 1, j, 1), 1, 0); if(j > 1) sol.AddEdge(pos(i, j, 1), pos(i, j - 1, 1), 1, 0); if(i < n) sol.AddEdge(pos(i, j, 1), pos(i + 1, j, 1), 1, 0); if(j < n) sol.AddEdge(pos(i, j, 1), pos(i, j + 1, 1), 1, 0); } else { sol.AddEdge(pos(i, j, 1), t, 1, 0); if(i > 1) sol.AddEdge(pos(i - 1, j, 2),pos(i, j, 1), 1, 0); if(j > 1) sol.AddEdge(pos(i, j - 1, 2),pos(i, j, 1), 1, 0); if(i < n) sol.AddEdge(pos(i + 1, j, 2), pos(i, j, 1), 1, 0); if(j < n) sol.AddEdge(pos(i, j + 1, 2), pos(i, j, 1), 1, 0); } } } sol.MaxCostFlow(s, t, m); cout << ans - sol.ans << endl; }
不知道为什么 在限制增广次数的时候 ZKW 的多路增广是错的