网络流之最小费最大流
网络流之最小费最大流
题目:n个人,m个房,要求每个人都回房间且路程和最短,每个人都回房间就是最大流为n,同时要求路程和最短就是还要要求费用最小了。
相较于最大流,要求跑最大流的同时要求费用最低,那么我们便不能够,将dfs的路径全部加入答案,因为里面费用可能会花的更多,所以我们只能降低效率,每次只增广一条最短路,用spfa找最短路,并记录路径后更新最大流于费用即可。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define ll long long
const ll inf=1e18;
int st,fi,n,m,tot,head[1000007],vis[1000007],pe[1000007],pn[1000007];
ll fw,dis[1000007],ans;
char s[107][107];
vector<pair<int,int> >H,M;
struct madoka{
int to;
int next;
ll w;
ll z;
}e[1000007];
void add(int u,int v,ll w,ll z){
e[++tot].to=v;
e[tot].next=head[u];
e[tot].w=w;
e[tot].z=z;
head[u]=tot;
e[++tot].to=u;
e[tot].next=head[v];
e[tot].w=0;
e[tot].z=-z;
head[v]=tot;
}
bool spfa(){
queue<int>sa;
for(int i=1;i<=fi;i++){
dis[i]=inf;
vis[i]=0;
}
sa.push(st);
dis[st]=0;
vis[st]=1;
while(!sa.empty()){
int p=sa.front();
sa.pop();
vis[p]=0;
for(int i=head[p];i!=0;i=e[i].next){
int to=e[i].to;
if(e[i].w&&dis[p]+e[i].z<dis[to]){
dis[to]=dis[p]+e[i].z;
pn[to]=p;
pe[to]=i;
if(vis[to]==0){
vis[to]=1;
sa.push(to);
}
}
}
}
if(dis[fi]==inf){
return 0;
}
return 1;
}
ll go(int p,ll now){
if(p==st)return now;
ll lin=go(pn[p],min(now,e[pe[p]].w));
e[pe[p]].w-=lin;
e[pe[p]^1].w+=lin;
return lin;
}
void dinic(){
fw=0;
ans=0;
while(spfa()){
ll now=go(fi,inf);
fw+=now;
ans+=dis[fi]*now;
}
}
void init(){
tot=1;
memset(head,0,sizeof(head));
M.clear();
H.clear();
}
int main(){
while(1){
init();
scanf("%d%d",&n,&m);
if(n==0&&m==0)break;
for(int i=1;i<=n;i++){
scanf("%s",s[i]+1);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]=='m'){
M.push_back({i,j});
}
if(s[i][j]=='H'){
H.push_back({i,j});
}
}
}
st=M.size()+H.size()+1;
fi=M.size()+H.size()+2;
for(int i=0;i<M.size();i++){
add(st,i+1,1,0);
}
for(int i=0;i<H.size();i++){
add(M.size()+i+1,fi,1,0);
}
for(int i=0;i<M.size();i++){
for(int j=0;j<H.size();j++){
add(i+1,M.size()+j+1,1,abs(M[i].first-H[j].first)+abs(M[i].second-H[j].second));
}
}
dinic();
printf("%lld\n",ans);
}
}