洛谷 P2045 方格取数加强版

题目描述

给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大

输入格式

第一行两个数n,k(1<=n<=50, 0<=k<=10)

接下来n行,每行n个数,分别表示矩阵的每个格子的数

输出格式

一个数,为最大和

输入输出样例

输入 #1
3 1
1 2 3
0 2 1
1 4 2
输出 #1
11

说明/提示

每个格子中的数不超过1000

 

思路:一道比较好的费用流题目。道理还是比较容易想的,但是我还是想了很长时间。。。

我个人的做法是这样的,对于一个格子,我们把它拆成4个点,分成:一个入点,一个出点,和两个中间点,入点到一个中间点连一条容量为1,边权为当前格子权值的边,到另一个中间点连一条容量为K-1,边权为0的边,两个中间点再向出点连各自的边,容量分别为1和K-1(均为inf也可以),边权均为0;

对于每一个出点,向右方向和下方向的格子的入点连容量为K,边权为0的边。最后源点和汇点分别向(1,1)(n,n)连边。

PS:我的做法其实过于麻烦,事实上只要拆成2个点即可。无需在拆中间点。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 using namespace std;
  7 const int N = 2e4 + 5;
  8 const int M = 1e5 + 5;
  9 struct edge{
 10     int from, to, cap, dis;
 11 }e[M << 1];
 12 int head[N], nxt[M << 1], tot = 1;
 13 void adde(int f, int t, int c, int d)
 14 {
 15     e[++ tot] = (edge){f, t, c, d};
 16     nxt[tot] = head[f];
 17     head[f] = tot;
 18 }
 19 int n, st, ed;
 20 const int inf = 0x3f3f3f3f;
 21 int dist[N], inq[N], pre[N];
 22 queue <int> Q;
 23 bool spfa()
 24 {
 25     memset(dist, 0x3f, sizeof dist);
 26     memset(inq, 0, sizeof inq);
 27     memset(pre, 0, sizeof pre);
 28     while(!Q.empty()) Q.pop();
 29     dist[st] = 0;
 30     inq[st] = 1;
 31     Q.push(st);
 32     while(!Q.empty())
 33     {
 34         int u = Q.front();
 35         Q.pop();
 36         inq[u] = 0;
 37         for(int i = head[u]; i; i = nxt[i])
 38         {
 39             int v = e[i].to;
 40             if(dist[v] > dist[u] + e[i].dis && e[i].cap)
 41             {
 42                 dist[v] = dist[u] + e[i].dis;
 43                 pre[v] = i;
 44                 if(!inq[v])
 45                 {
 46                     inq[v] = 1;
 47                     Q.push(v);
 48                 }
 49             }
 50         }
 51     }
 52     return (dist[ed] != inf);
 53 }
 54 int cost = 0;
 55 void redate()
 56 {
 57     int fl = inf;
 58     for(int i = pre[ed]; i; i = pre[e[i].from]) fl = min(fl, e[i].cap);
 59     for(int i = pre[ed]; i; i = pre[e[i].from])
 60     {
 61         e[i].cap -= fl;
 62         e[i^1].cap += fl;
 63         cost += fl * e[i].dis;
 64     }
 65 }
 66 void mcmf()
 67 {
 68     while(spfa()) redate();
 69 }
 70 int K;
 71 int num[N];
 72 void solve()
 73 {
 74     st = 0, ed = 5*n*n;
 75     adde(st, 1, K, 0);
 76     adde(1, st, 0, 0);
 77     adde(4*n*n, ed, K, 0);
 78     adde(ed, 4*n*n, 0, 0);
 79     for(int i = 1; i <= n*n; i ++)
 80     {
 81         adde(i, i + n*n, 1, -num[i]);
 82         adde(i + n*n, i, 0, num[i]);//(1,2
 83         adde(i, i + 2*n*n, K - 1, 0);
 84         adde(i + 2*n*n, i, 0, 0);//(1,3
 85         adde(i + n*n, i + 3*n*n, 1, 0);
 86         adde(i + 3*n*n, i + n*n, 0, 0);//(2,4
 87         adde(i + 2*n*n, i + 3*n*n, K - 1, 0);
 88         adde(i + 3*n*n, i + 2*n*n, 0, 0);//(3,4
 89     }
 90     for(int i = 1; i <= n*n; i ++)
 91     {
 92         if(i % n)
 93         {
 94             adde(i + 3*n*n, i + 1, K, 0);
 95             adde(i + 1, i + 3*n*n, 0, 0);
 96         }
 97         if(i <= n*(n - 1))
 98         {
 99             adde(i + 3*n*n, i + n, K, 0);
100             adde(i + n, i + 3*n*n, 0, 0);
101         }
102     }
103 }
104 int main()
105 {
106     scanf("%d%d", &n, &K);
107     for(int i = 1; i <= n; i ++)
108         for(int j = 1; j <= n; j ++)
109             scanf("%d", &num[j + (i - 1)*n]);
110     solve();
111     mcmf();
112     printf("%d\n", -cost);
113     return 0;
114 }

 

posted @ 2019-10-10 19:28  Frank喵^_^  阅读(203)  评论(0编辑  收藏  举报