题意:
有个方阵,每个格子里都有一个非负数,从左上角走到右下角,每次走一步,只能往右或往下走,经过的数字拿走
每次都找可以拿到数字和最大的路径走,走k次,求最大和 这是 最大费用最大流 问题 每次spfa都找的是一条和最大的路径 s--到左上角的边流量是K限制增广次数
求最大费用最大流只需要把费用换成相反数,用最小费用最大流求解即可
构图过程:如下图
每个点拆分成两个 a a' 之间建两条边(当然还要建退边),分别是 (费用为该点相反数,流量为1) (费用为0,流量为k-1)
路过这点时,可以通过前边那条边拿到数字,
以后再从这儿过,就没有数字可拿了,走的就是第二条边
然后是 没点向 右和下 建一条边 费用0,流量k
然后s、t连接左上角和右下角即可
求最小费用最大流的方法和EK求最大流的方法类似,
都是找一条增广路径,然后把流加进去
只不过EK找的是路径最短的增广路(广搜即可得)
而最小费用最大流 找的是费用最小的路径(对费用求最短路)
我用了第一种
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
struct node
{
int u,v,w,f,next;
} edge[50000];
int s,T,cnt;
int head[55000],pre[55000],vis[50500],dis[55000];
void add(int u,int v,int w,int f)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].f=f;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].u=v;
edge[cnt].v=u;
edge[cnt].w=0;
edge[cnt].f=-f;
edge[cnt].next=head[v];
head[v]=cnt++;
}
int SPFA()
{
int i;
memset(pre,-1,sizeof(pre));
memset(vis,0,sizeof(vis));
for(i=0; i<=cnt; i++)
dis[i]=-1; //求最小费用是要全部初始化为INF
queue<int>q;
dis[s]=0;
vis[s]=1;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
i=head[u];
vis[u]=0;
while(i!=-1)
{
if(edge[i].w>0&&dis[edge[i].v]<dis[u]+edge[i].f) //这个地方求最小费用是要>
{
dis[edge[i].v]=dis[u]+edge[i].f;
pre[edge[i].v]=i;
if(!vis[edge[i].v])
{
vis[edge[i].v]=1;
q.push(edge[i].v);
}
}
i=edge[i].next;
}
}
if(pre[T]==-1)
return 0;
return 1;
}
int MincostMaxFlow()
{
int ans=0;
while(SPFA())
{
int maxl=INF;
int p=pre[T];
while(p!=-1)
{
maxl=min(maxl,edge[p].w);
p=pre[edge[p].u];
}
//printf("%d\n",maxl);
p=pre[T];
while(p!=-1)
{
edge[p].w-=maxl;
edge[p^1].w+=maxl;
//printf("%d\n",edge[p].f);
ans+=maxl*edge[p].f;
p=pre[edge[p].u];
}
}
return ans;
}
int main()
{
int n,k;
int map[55][55];
int a,b;
while(~scanf("%d %d",&n,&k))
{
cnt=0;
memset(head,-1,sizeof(head));
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
scanf("%d",&map[i][j]);
/*for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
printf("%d ",map[i][j]);
printf("\n");
}*/
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
{
a=(i-1)*n+j;
b=a+n*n;
add(a,b,1,map[i][j]);
add(a,b,INF,0);
if(i<n)
add(b,a+n,k,0);
if(j<n)
add(b,a+1,k,0);
}
/* for(int i=0;i<cnt;i++)
printf("%d ",edge[i].f);
printf("\n");*/
s=0;
T=2*n*n+1;
add(s,1,k,0);
add(2*n*n,T,k,0);
int ans=0;
ans+=MincostMaxFlow();
printf("%d\n",ans);
}
return 0;
}
具体变化在模版中已经写出来了,这道题还学到了一个建图方式就是拆点为的就是控制走多少遍与他的权值的关系