POJ3422费用流

时间:2022-03-08 07:06:05

Description:
  一个N * N的奖赏地图,你可以走k次这个地图,但是每一次你走过一个有分的节点,你获得得分,但这个节点的得分都要清零,问你走k次地图的最大得分

Solution:

  把得分变成负数就变成最小费用问题了,看看题目的要求进行建图,首先每个节点的访问次数最多也就k次,然后有分的点访问一次后就没分了,可以变成1,k-1的访问,所以转换成经典的拆点建图问题

  ·源点到入口 —— 容量k —— 费用0

  ·出口到汇点 —— 容量k —— 费用0

  ·地图中的节点拆分成x1,x2

    ·x1 - x2 加两次边 一次:容量为1 —— 费用是 - cost 另一次是 容量是k - 1 —— 费用是0

    ·x2 到其它可达点 —— 容量k —— 费用 0

所以写这篇博客目的也就是学习一下,生活条件的建图思想,把分数只能走一次转化成建两类边的思想,是精彩点

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define inf (1 << 28)
using namespace std;
const int maxn = 50500;
const int maxm = 100100; int n,m,k; struct node
{
int to,val,cost,pre;
}e[maxm];
int id[maxn];
int cnt;
void add(int from,int to,int val,int cost)
{
e[cnt].to = to;
e[cnt].val = val;
e[cnt].cost = cost;
e[cnt].pre = id[from];
id[from] = cnt++;
swap(from,to);
e[cnt].to = to;
e[cnt].val = 0;
e[cnt].cost = -cost;
e[cnt].pre = id[from];
id[from] = cnt++;
}
void init()
{
memset(id,-1,sizeof(id));
cnt = 0;
} int dis[maxn];
int pre[maxn];
int path[maxn];
int vis[maxn]; bool spfa(int s,int t)
{
memset(pre,-1,sizeof(pre));
memset(vis,0,sizeof(vis));
for(int i = 0;i < maxn ;++i)
dis[i] = inf;
dis[s] = 0;
vis[s] = 1;
queue<int> q;
q.push(s);
while(q.size())
{
int now = q.front();
q.pop(); for(int i = id[now];~i;i = e[i].pre)
{
int to = e[i].to;
int val = e[i].val;
int cost = e[i].cost; if(val > 0 && dis[now] + cost < dis[to])
{
dis[to] = dis[now] + cost;
pre[to] = now;
path[to] = i;
if(!vis[to])
{
vis[to] = 1;
q.push(to);
}
}
}
vis[now] = 0;
}
if(pre[t] == -1)return false;
return true;
}
int mcmf(int s,int t)
{
int c = 0;
while(spfa(s,t))
{
int mf = inf;
for(int now = t;now != s;now = pre[now])
{
if(e[path[now]].val < mf)
mf = e[path[now]].val;
}
for(int now = t;now != s;now = pre[now])
{
e[path[now]].val -= mf;
e[path[now]^1].val += mf;
c += mf * e[path[now]].cost;
}
}
return c;
} int coss[55][55];
int main()
{
while(~scanf("%d%d",&n,&k))
{
init();
int s = 0;
int t = 2 * n * n + 1;
for(int i = 1;i <= n;++i)
{
for(int j = 1;j <= n;++j)
{
scanf("%d",&coss[i][j]);
}
}
add(s,1,k,0);
add(2*n*n,t,k,0);
for(int i = 1;i <= n;++i)
{ for(int j = 1;j <= n;++j)
{
int id1 = (i - 1) * n + j;
int id2 = n * n + id1; add(id1,id2,1,-coss[i][j]);
add(id1,id2,k-1,0);
if(i == n && j == n)continue;
if(i + 1 > n)
add(id2,id1+1,k,0);
else if(j + 1 > n)
add(id2,id1+n,k,0);
else
{
add(id2,id1+n,k,0);
add(id2,id1+1,k,0);
}
}
}
printf("%d\n",mcmf(s,t) * -1 );
}
return 0;
}