【POJ3422】Kaka's Matrix Travels【费用流】
题目大意:
题目链接:http://poj.org/problem?id=3422
次传纸条。
传纸条问题都不知道的话那我也就没办法了。
真香
思路:
次传纸条也是一个比较经典的题目。它的解法是费用流。
如果重复经过的格子是重复计分的,那么就是一个很裸的费用流了(其实答案就是一次传纸条),着重思考如何处理“重复的格子不重复计分”。
那么显然是需要拆点的。把每一个点拆成和,分别表示入点和出点。
考虑如何在这两个点之间连边。显然,这两个点中间一共要连条边,因为最多会经过次。但是只有其中1条边是有费用的,其他条边都是没有费用的。
那么显然要在之间连两种边:
- 一条流量为1,费用为这个格子的权值的边
- 一条流量为费用为0的边
这样就可以有效解决重复的格子不重复计分的问题了。
接下来的连边就非常显然了。
- (点1的入点),流量,费用0
- (最后一个点的出点,总共有个点),流量,费用0
- ,流量,费用0
- ,流量,费用0
跑最大费用最大流就可以了。
代码:
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=5010,M=200010;
int n,m,x,S,T,tot=1,head[N],dis[N],pre[N];
bool vis[N];
struct edge
{
int next,to,flow,cost,from;
}e[M];
void add(int from,int to,int flow,int cost)
{
e[++tot].to=to;
e[tot].from=from;
e[tot].flow=flow;
e[tot].cost=cost;
e[tot].next=head[from];
head[from]=tot;
}
bool spfa() //费用流模板(spfa,addflow,mcmf)
{
memset(dis,0xcf,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
dis[S]=0,vis[S]=1;
queue<int> q;
q.push(S);
while (q.size())
{
int u=q.front(),v;
q.pop();
vis[u]=0;
for (int i=head[u];~i;i=e[i].next)
{
v=e[i].to;
if (e[i].flow&&dis[v]<dis[u]+e[i].cost)
{
dis[v]=dis[u]+e[i].cost;
pre[v]=i;
if (!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
return dis[T]>0;
}
int addflow()
{
int minflow=0x3f3f3f3f;
for (int i=T;i!=S;i=e[pre[i]].from)
minflow=min(minflow,e[pre[i]].flow);
for (int i=T;i!=S;i=e[pre[i]].from)
{
e[pre[i]].flow-=minflow;
e[pre[i]^1].flow+=minflow;
}
return minflow*dis[T];
}
int mcmf()
{
int cost=0;
while (spfa())
cost+=addflow();
return cost;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for (int i=1;i<=n*n;i++)
{
scanf("%d",&x);
add(i,i+n*n,1,x);
add(i+n*n,i,0,-x);
add(i,i+n*n,m-1,0);
add(i+n*n,i,0,0);
if (i+n<=n*n)
{
add(i+n*n,i+n,m,0);
add(i+n,i+n*n,0,0);
}
if (i%n)
{
add(i+n*n,i+1,m,0);
add(i+1,i+n*n,0,0);
}
}
S=n*n*2+1;
T=n*n*2+2;
add(S,1,m,0);
add(1,S,0,0);
add(n*n*2,T,m,0);
add(T,n*n*2,0,0);
printf("%d",mcmf());
return 0;
}