[洛谷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 }

 

posted @ 2019-02-12 22:38  AC-Evil  阅读(162)  评论(0编辑  收藏  举报