POJ 3422 Kaka's Matrix Travels (K取方格数:最大费用流)

时间:2021-05-02 05:36:59

题意

给出一个n*n大小的矩阵,要求从左上角走到右下角,每次只能向下走或者向右走并取数,某位置取过数之后就只为数值0,现在求解从左上角到右下角走K次的最大值.

思路

经典的费用流模型:K取方格数。

构图方法:将矩阵的每个元素m[i][j]拆成两个点u=(i-1)*n+j和v=n*n+(i-1)*n+j,从u到v连两条边:
1> 连边(u,v),容量为1,费用值为m[i][j],这样可以保证每一个位置的数只被取一次
2> 连边(u,v),容量为INF,费用值为0,这样可以保证某位置取数被置为0之后,随便怎么取对最后的费用值不会产生影响
由于每次只能向下走或者向右走,所以需要连两条边:
1> 向下走:即从(i,j)到(i+1,j),连边v=n*n+(i-1)*n+j到i*n+j,容量为INF,费用为0
2> 向右走:即从(i,j)到(i,j+1),连边v=n*n+(i-1)*n+j到(i-1)*n+j+1,容量为INF,费用为0.
然后为了限制只走K次,需要添加源点s=0和汇点t=2*n*n+1,连边:s……矩阵左上角顶点1,容量为k,费用为0,连边矩阵右下角顶点被拆之后对应的顶点2*n*n……t,容量为K,费用为0,然后对所建立的图求解最大费用流即可.

代码

[cpp]
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <algorithm>
#include <string>
#include <queue>
#include <cstring>
#define MID(x,y) ((x+y)/2)
#define MEM(a,b) memset(a,b,sizeof(a))
#define REP(i, begin, m) for (int i = begin; i < begin+m; i ++)
using namespace std;

const int MAXV = 200005;
const int MAXE = 100005;
const int oo = 0x3fffffff;

template <class NodesType>
struct Nodes{
NodesType dist;
int pre, head; //pre存前趋边, head存前向星
bool visit;
};
template <class EdgesType>
struct Edges{
int u, v, next;
EdgesType cost, flow;
};
template <class T>
struct MinCostMaxFlow{
Nodes <T> node[MAXV];
Edges <T> arc[2*MAXE];
int vn, en;
void init(int n){
vn = n;
en = 0;
for (int i = 0; i <= n; i ++){
node[i].head = -1;
}
}
void insert_flow(int u, int v, T flow, T cost){
arc[en].u = u;
arc[en].v = v;
arc[en].flow = flow;
arc[en].cost = cost;
arc[en].next = node[u].head;
node[u].head = en ++;
arc[en].v = u;
arc[en].u = v;
arc[en].flow = 0;
arc[en].cost = -cost;
arc[en].next = node[v].head;
node[v].head = en ++;
}
void print_edges(){
for (int i = 0; i < en; i ++){
printf("u = %d v = %d flow = %d cost = %d\n", arc[i].u, arc[i].v, arc[i].flow, arc[i].cost);
}
}
queue <int> q;
bool spfa(int s, int t){
for (int i = 1; i <= vn; i ++){
node[i].dist = oo;
node[i].pre = -1;
node[i].visit = false;
}
node[s].dist = 0;
node[s].visit = true;
q.push(s);
while(!q.empty()){
int u = q.front();
q.pop();
node[u].visit = false;
for (int i = node[u].head; i != -1; i = arc[i].next){
int v = arc[i].v;
if (arc[i].flow > 0 && node[v].dist > node[u].dist + arc[i].cost){
node[v].dist = node[u].dist + arc[i].cost;
node[v].pre = i;
if (!node[v].visit){
node[v].visit = true;
q.push(v);
}
}
}
}
if (node[t].pre == -1)
return 0;
else
return 1;
}
T solve(int s, int t, T &mincost){
mincost = 0;
T maxflow = 0;
while(spfa(s, t)){
T minflow = oo;
for (int i = node[t].pre; i != -1; i = node[arc[i].u].pre){
minflow = min(minflow, arc[i].flow);
}
for (int i = node[t].pre; i != -1; i = node[arc[i].u].pre){
arc[i].flow -= minflow;
arc[i^1].flow += minflow;
mincost += arc[i].cost * minflow;
}
maxflow += minflow;
}
return maxflow;
}
};
MinCostMaxFlow <int> mcmf;

int map[55][55];
int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int n, k;
while(scanf("%d %d", &n, &k) != EOF){
mcmf.init(2*n*n+2);
mcmf.insert_flow(2*n*n+1, 1, k, 0);
mcmf.insert_flow(2*n*n, 2*n*n+2, k, 0);
REP(i, 1, n)
REP(j, 1, n){
scanf("%d", &map[i][j]);
int u = (i-1)*n+j;
mcmf.insert_flow(u, u+n*n, 1, -map[i][j]);
mcmf.insert_flow(u, u+n*n, oo, 0);
if (i < n){
mcmf.insert_flow(u+n*n, u+n, oo, 0);
}
if (j < n){
mcmf.insert_flow(u+n*n, u+1, oo, 0);
}
}
//mcmf.print_edges();
int res = 0;
mcmf.solve(2*n*n+1, 2*n*n+2, res);
printf("%d\n", -res);
}
return 0;
}
[/cpp]