[洛谷P2045]方格取数加强版
题目传送门
还记得原版方格取数怎么做的吧?$DP$。
当时的复杂度为$O(n^4)$。
但现在$k \leq 10$,所以原办法行不通。
这道题发现一个数只能被取一次。
然后就是找$k$条从$(1,1)$到$(n,n)$的路径使得覆盖的数值和最大。
这个可以用网络流的相关知识求解(准确说是最小费用最大流)。
将每个坐标的点拆成两个点$(x,y,a)$和$(x,y,b)$,表示该点的入点和出点。
因为原图中可以向下走或向右走,所以连边$(x,y,b)\ \rightarrow\ (x+1,y,a)\ \&\&\ (x,y+1,a)$ ,流量为$INF$。费用为$0$(因为可以走无限次,且不产生贡献)
连边$(x,y,a)$到$(x,y,b)$,流量为$1$,费用为对应方格的数$-A_{xy}$,满足了只取一次的要求,还要满足能再次经过且不产生贡献,所以再连一条边,流量为$INF$,费用为$0$。
然后建超级源和超级汇$(s)$和$(t)$,连边$(s)\ \rightarrow\ (1,1,a)$,流量为$k$,费用为$0$;连边$(n,n,b)\ \rightarrow\ (t)$,流量为$k$,费用为$0$。
跑一遍最小费用最大流即可。
这里用了一个技巧:将实际的贡献设成负数。这样就会尽可能流过这个结点获得负的贡献达到目的。
所以要用$SPFA$的网络流算法($SPFA$:我还没扑街233)。
最后注意输出费用的相反数即可。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define re register 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i) 7 #define repd(i, a, b) for (re int i = a; i >= b; --i) 8 #define maxx(a, b) a = max(a, b); 9 #define minn(a, b) a = min(a, b); 10 #define LL long long 11 #define INF (1 << 30) 12 13 inline int read() { 14 int w = 0, f = 1; char c = getchar(); 15 while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); 16 while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar(); 17 return w * f; 18 } 19 20 const int maxn = 50 * 50 + 5; 21 22 struct Edge { 23 int u, v, c, f, w, pre; 24 }; 25 26 struct Graph { 27 Edge ed[maxn << 3]; 28 int n, m, G[maxn << 1]; 29 void init(int n) { 30 this->n = n; 31 m = 1; 32 memset(G, 0, sizeof(G)); 33 } 34 void add(int u, int v, int c, int w) { 35 ed[++m] = (Edge){u, v, c, 0, w, G[u]}; 36 G[u] = m; 37 ed[++m] = (Edge){v, u, 0, 0, -w, G[v]}; 38 G[v] = m; 39 } 40 int a[maxn << 1], d[maxn << 1], inq[maxn << 1], p[maxn << 1]; 41 int s, t; 42 queue<int> Q; 43 bool bellmanford(int &flow, int &cost) { 44 memset(inq, 0, sizeof(inq)); 45 rep(i, 1, n) d[i] = INF; 46 inq[s] = 1; Q.push(s); p[s] = 0; a[s] = INF; d[s] = 0; 47 while (!Q.empty()) { 48 int u = Q.front(); Q.pop(); inq[u] = 0; 49 for (register int i = G[u]; i; i = ed[i].pre) { 50 Edge &e = ed[i]; 51 if (e.f < e.c && d[u] + e.w < d[e.v]) { 52 d[e.v] = d[u] + e.w; 53 a[e.v] = min(a[u], e.c-e.f); 54 p[e.v] = i; 55 if (!inq[e.v]) { 56 Q.push(e.v); 57 inq[e.v] = 1; 58 } 59 } 60 } 61 } 62 if (d[t] == INF) return false; 63 flow += a[t]; 64 cost += a[t] * d[t]; 65 for (register int i = t; p[i]; i = ed[p[i]].u) { 66 ed[p[i]].f += a[t]; 67 ed[p[i]^1].f -= a[t]; 68 } 69 return true; 70 } 71 int mincostmaxflow(int s, int t, int &cost) { 72 this->s = s, this->t = t; 73 int flow = 0; cost = 0; 74 while (bellmanford(flow, cost)); 75 return flow; 76 } 77 } G; 78 79 int n, k; 80 81 #define id(x, y) ((x)*n+(y)-n) 82 83 int main() { 84 n = read(), k = read(); 85 G.init(n*n*2+2); 86 rep(i, 1, n) 87 rep(j, 1, n) { 88 //a[i][j] = read(); 89 G.add(id(i, j), id(i, j)+n*n, 1, -read()); 90 G.add(id(i, j), id(i, j)+n*n, k, 0); 91 if (i != n) G.add(id(i, j)+n*n, id(i+1, j), k, 0); 92 if (j != n) G.add(id(i, j)+n*n, id(i, j+1), k, 0); 93 } 94 G.add(n*n*2+1, 1, k, 0); 95 G.add(n*n*2, n*n*2+2, k, 0); 96 int cost; 97 G.mincostmaxflow(n*n*2+1, n*n*2+2, cost); 98 printf("%d", -cost); 99 return 0; 100 }