题目在《挑战程序设计竞赛》的练习题的最小费用流分类里。看到后第一反应是dp,想到了 51Nod 1084 矩阵取数问题 V2 这个题,这个题是个多进程dp,从左上走到右下,走两次。然后就去搜了下这个题的题解:http://www.hankcs.com/program/algorithm/poj-3422-kakas-matrix-travels.html 主要是不怎么会建图,看了下题解的注释,然后画了画,突然想到怎么建图了,然后就是建图,模板,ac。。。。
看到好多题解把这个题分类到 最小费用最大流,感觉分错类了吧,这个是 最小费用流啊,给定固定的流量,求最小花费。 最小费用最大流应该是求最大流里面花费最小的吧。。。。
思路:拆点,建图,每个点拆成两个点,中间连一条边,容量为1,花费为那点的值。看完题最大的疑惑就是k,0<=k<=10,就只能向左向下走两次,k怎么还能到10???看了题解才发现可以重复走,重复走就是把一个点拆成两个点时,中间加两条边,一条边容量为1,花费为那点的值。另一条边容量为k,花费为0,这样就可以重复走了。
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
struct edge
{
int to,cap,cost,rev;
edge(int _to = 0, int _cap = 0, int _cost = 0, int _rev = 0)
:to(_to),cap(_cap),cost(_cost),rev(_rev) {}
};
const int INF = 0x3f3f3f3f;
const int MAX_V = 5555;
int V;
vector<edge> G[MAX_V];
int dist[MAX_V];
int prevv[MAX_V],preve[MAX_V];
void add_edge(int from, int to, int cap, int cost)
{
G[from].push_back(edge(to,cap,cost,G[to].size()));
G[to].push_back(edge(from,0,-cost,G[from].size()-1));
}
int min_cost_flow(int s, int t, int f)
{
int res = 0;
while(f > 0)
{
memset(dist,0x3f,sizeof(dist));
dist[s] = 0;
bool update = true;
while(update)
{
update = false;
for(int v = 0; v < V; ++v)
{
if(dist[v] == INF) continue;
for(int i = 0; i < G[v].size(); ++i)
{
edge& e = G[v][i];
if(e.cap > 0 && dist[e.to] > dist[v] + e.cost)
{
dist[e.to] = dist[v] + e.cost;
prevv[e.to] = v;
preve[e.to] = i;
update = true;
}
}
}
}
if(dist[t] == INF)
return -1;
int d = f;
for(int v = t; v != s; v = prevv[v])
d = min(d,G[prevv[v]][preve[v]].cap);
f -= d;
res += d*dist[t];
for(int v = t; v != s; v = prevv[v])
{
edge& e = G[prevv[v]][preve[v]];
e.cap -= d;
G[v][e.rev].cap += d;
}
}
return res;
}
const int MAXN = 55;
int GG[MAXN][MAXN];
int main()
{
int n,k,s,t,cur1,cur2,nexth,nextv;
scanf("%d %d",&n,&k);
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
scanf("%d",&GG[i][j]);
V = 2*n*n+2;
for(int i = 0; i <= V; ++i) G[i].clear();
s = 0,t = V-1;
add_edge(s,1,k,0);//连接到源点
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < n; ++j)
{
cur2 = i*(2*n) + (j+1)*2;
cur1 = cur2 - 1;
add_edge(cur1,cur2,1,-GG[i][j]);
add_edge(cur1,cur2,k,0);
if(i+1 < n)
{
nextv = (i+1)*(2*n) + (j+1)*2 - 1;
add_edge(cur2,nextv,k,0);
}
if(j+1 < n)
{
nexth = i*(2*n) + (j+2)*2 - 1;
add_edge(cur2,nexth,k,0);
}
}
}
add_edge(t-1,t,k,0);//连接到汇点
int res = min_cost_flow(s,t,k);
printf("%d\n",-res);
return 0;
}