解题思路
新算法之:最小费用最大流问题
又是一个我不会的算法啊啊啊啊啊 ,因为不知道如何讲解,且这篇博客十分详细,就不加描述了。
看完算法之后,回来这道题,先考虑构图:
对于每一行,用一个点表示,因为每行最多选两块地,所以从源点
s
s
s向这
n
n
n个行点连一条容量为
2
2
2,费用为
0
0
0的边。
对于每一列,同样用一个点表示,因为每列最多选两块地,所以从这
m
m
m个列点向汇点
t
t
t连一条容量为
2
2
2,费用为
0
0
0的边。
最后,因为在同一行、同一列最多只有一块的,所以,从
n
n
n个行点向
m
m
m个列点连一条容量为
1
1
1,费用为
W
[
i
,
j
]
W[i,j]
W[i,j]的边。
代码
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int n,m;
int ans,f[100][100],c[100][100],dis[100100],next[100100],v[100100],q[100100];//f[i][j]表示点i,j间的流量,c[i][j]表示i,j间的花费
bool spfa(){
memset(next,0,sizeof(next));
memset(v,0,sizeof(v));
int h=0,t=1;
for(int i=0;i<=n+m+1;i++)
dis[i]=-2147483600;
q[1]=0,dis[0]=0;
while(h<t)
{
int x=q[++h];
v[x]=0;
for(int i=0;i<=n+m+1;i++)
{
if(f[x][i]&&dis[i]<dis[x]+c[x][i])//求最大花费:松弛
{
dis[i]=dis[x]+c[x][i];
next[i]=x;
if(!v[i])
{
q[++t]=i;
v[i]=1;
}
}
}
}
if(dis[n+m+1]<=0)return 0;//判断是否成为一条可行流
return 1;
}
void work(){
int sum=2147483600;
for(int i=n+m+1;i;i=next[i])
sum=min(sum,f[next[i]][i]);//求最大流量
for(int i=n+m+1;i;i=next[i])
{
f[next[i]][i]-=sum;
f[i][next[i]]+=sum;//更新反边,让其可以反悔
ans+=c[next[i]][i]*sum;//统计花费
}
}
int main(){
freopen("pick.in","r",stdin);
freopen("pick.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
f[0][i]=2;
for(int i=1;i<=m;i++)
f[n+i][n+m+1]=2;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&c[i][n+j]);
c[n+j][i]=-c[i][n+j];
f[i][n+j]=1;
}
}//建图
while(spfa())work();
printf("%d",ans);
}
/*
8 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7 8
*/