您的位置:首页 > 其它

POJ 3422 Kaka's Matrix Travels【最大费用流】

2015-07-20 17:20 537 查看
意:给定N,K,和N*N矩阵表示该点权值,从左上到右下,求走K次所得的权值和最大

最大费用流:把最小费用流的权值改为负数,输出也加个负数就行。

建图:因为权值附在点上,所以拆点是必须的,然后点A到点A'要两条边,一条的费用为权值,容量为1,另一条费用为0,容量为K-1,使得走一次时有权,走其他K-1次时没有(每个点最多K次访问)。建立一个源点连接一号点,容量为K,费用0,汇点同理。然后每个点跟自己的左上两点建边(如果有左上的话),容量为K,费用0。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) )
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);

//模板大法好
const int inf = 0x3f3f3f3f;
const int maxn = 5025;
const int maxm = 30000;
struct Edge {
int to, next, cap, flow, cost;
} edge[maxm * 2];
int head[maxn], tol;
int pre[maxn], dis[maxn];
bool vis[maxn];
void init() {
tol = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int cap, int cost) {
edge[tol].to = v;
edge[tol].cap = cap;
edge[tol].cost = cost;
edge[tol].flow = 0;
edge[tol].next = head[u];
head[u] = tol++;
edge[tol].to = u;
edge[tol].cap = 0;
edge[tol].cost = -cost;
edge[tol].flow = 0;
edge[tol].next = head[v];
head[v] = tol++;
}
bool spfa(int s, int t) {
queue<int>q;
memset(dis, inf, sizeof(dis));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
dis[s] = 0;
vis[s] = true;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (edge[i].cap > edge[i].flow &&
dis[v] > dis[u] + edge[i].cost ) {
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
return pre[t] != -1;
}
//返回最大流,cost存最小费用
int minCostMaxflow(int s, int t, int &cost) {
int flow = 0;
cost = 0;
while (spfa(s, t)) {
int Min = inf;
for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
if (Min > edge[i].cap - edge[i].flow) {
Min = edge[i].cap - edge[i].flow;
}
}
for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to]) {
edge[i].flow += Min;
edge[i ^ 1].flow -= Min;
cost += edge[i].cost * Min;
}
flow += Min;
}
return flow;
}
int main() {
int val;
int n, k;
//    freopen("1.in","r",stdin);
while (~scanf("%d%d",&n,&k)) {
int ss = 0, tt = 2*n*n + 1;     //包含了拆点,所以是2*n*n
init();
int cnt=1;
FOR(i,1,n)
{
FOR(j,1,n)
{
scanf("%d",&val);
val=-val;   //最大流取负数
addEdge(cnt,n*n+cnt,1,val);     //A对A'建边
addEdge(cnt,n*n+cnt,k-1,0);     //A对A'建边
if(j!=1)
addEdge(cnt-1+n*n,cnt,k,0); //不是第一列就跟做左一列的建边
if(i!=1)
addEdge(cnt-n+n*n,cnt,k,0); //不是第一行就对上一行的建边
cnt++;
}
}

addEdge(ss,1,k,0);      //源点向1号点建K容量边
addEdge(n*n*2,tt,k,0);  //汇点向终号点建K容量边

int cost;
minCostMaxflow(ss, tt, cost);
printf("%d\n",-cost);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: