题目大意:
题目链接:http://poj.org/problem?id=3422
k
k
k次传纸条。
传纸条问题都不知道的话那我也就没办法了。
真香
思路:
k
k
k次传纸条也是一个比较经典的题目。它的解法是费用流。
如果重复经过的格子是重复计分的,那么就是一个很裸的费用流了(其实答案就是一次传纸条
×
k
\times k
×k),着重思考如何处理“重复的格子不重复计分”。
那么显然是需要拆点的。把每一个点
a
a
a拆成
a
1
a_1
a1和
a
2
a_2
a2,分别表示入点和出点。
考虑如何在这两个点之间连边。显然,这两个点中间一共要连
k
k
k条边,因为最多会经过
k
k
k次。但是只有其中1条边是有费用的,其他
k
−
1
k-1
k−1条边都是没有费用的。
那么显然要在
a
1
,
a
2
a_1,a_2
a1,a2之间连两种边:
- 一条流量为1,费用为这个格子的权值的边
- 一条流量为 k − 1 k-1 k−1费用为0的边
这样就可以有效解决重复的格子不重复计分的问题了。
接下来的连边就非常显然了。
- S → 1 1 S\to1_1 S→11(点1的入点),流量 m m m,费用0
- n 2 2 → T {n^2}_2\to T n22→T(最后一个点的出点,总共有 n 2 n^2 n2个点),流量 m m m,费用0
- i 2 → ( i + n ) 1 i_2\to (i+n)_1 i2→(i+n)1,流量 m m m,费用0
- i 2 → ( i + 1 ) 1 i_2\to (i+1)_1 i2→(i+1)1,流量 m m m,费用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;
}