poj 2195 二分图带权匹配+最小费用最大流

时间:2023-03-08 19:32:53
poj 2195 二分图带权匹配+最小费用最大流

题意:有一个矩阵,某些格有人,某些格有房子,每个人可以上下左右移动,问给每个人进一个房子,所有人需要走的距离之和最小是多少。

貌似以前见过很多这样类似的题,都不会,现在知道是用KM算法做了

KM算法目前还没弄懂,先套模板做

Sample Input

2 2
.m
H.
5 5
HH..m
.....
.....
.....
mm..H
7 8
...H....
...H....
...H....
mmmHmmmm
...H....
...H....
...H....
0 0

Sample Output

2
10
28
 #include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
const int MAXN=;
const int INF=0x3fffffff;
int g[MAXN][MAXN],match1[MAXN],match2[MAXN];
int KM(int m,int n)
{
int i,j,k,p,q;
int l1[MAXN],l2[MAXN];
int s[MAXN],t[MAXN];
int ret=;
for(i=;i<m;i++)
{
l1[i]=-INF;
for(j=;j<n;j++)
if(g[i][j]>l1[i])
l1[i]=g[i][j];
if(l1[i]==-INF) return -;//无法匹配
}
for(i=;i<n;i++)l2[i]=; memset(match1,-,sizeof(match1));
memset(match2,-,sizeof(match2));
for(i=;i<m;i++)
{
memset(t,-,sizeof(t));
for(s[p=q=]=i;p<=q&&match1[i]<;p++)
for(k=s[p],j=;j<n&&match1[i]<;j++)
if(l1[k]+l2[j]==g[k][j]&&t[j]<)
{
s[++q]=match2[j],t[j]=k;
if(s[q]<)
for(p=j;p>=;j=p)
match2[j]=k=t[j],p=match1[k],match1[k]=j;
} if(match1[i]<)
{
for(i--,p=INF,k=;k<=q;k++)
for(j=;j<n;j++)
if(t[j]<&&l1[s[k]]+l2[j]-g[s[k]][j]<p)
p=l1[s[k]]+l2[j]-g[s[k]][j]; for(j=;j<n;j++)if(t[j]>=)l2[j]+=p;
for(k=;k<=q;k++)l1[s[k]]-=p;
}
}
for(i=;i<m;i++)
{
if(match1[i]<)return -;//无法匹配
if(g[i][match1[i]]<=-INF)return -;
ret+=g[i][match1[i]];
}
return ret;
}
struct Node
{
int x,y;
};
Node node1[MAXN],node2[MAXN];
char str[MAXN];
int main()
{
int n,m;
int nx,ny;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==&&m==)break;
nx=;
ny=;
for(int i=;i<n;i++)
{
scanf("%s",&str);
for(int j=;j<m;j++)
{
if(str[j]=='m')
{
node1[nx].x=i;
node1[nx].y=j;
nx++;
}
else if(str[j]=='H')
{
node2[ny].x=i;
node2[ny].y=j;
ny++;
}
}
}
for(int i=;i<nx;i++)
for(int j=;j<ny;j++)
{
g[i][j]=-abs(node1[i].x-node2[j].x)-abs(node1[i].y-node2[j].y);
}
printf("%d\n",-KM(nx,ny));
}
return ;
}
 /*
POJ 2195 Going Home
邻接矩阵形式最小费用最大流
*/ #include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std; //************************************************************
//最小费用最大流算法
//SPFA求最短路
//邻接矩阵形式
//初始化:cap:容量,没有边为0
//cost:耗费,对称形式,没有边的也为0
//c是最小费用
//f是最大流
//*******************************************************
const int MAXN=;
const int INF=0x3fffffff;
int cap[MAXN][MAXN];//容量,没有边为0
int flow[MAXN][MAXN];
//耗费矩阵是对称的,有i到j的费用,则j到i的费用为其相反数
int cost[MAXN][MAXN]; int n;//顶点数目0~n-1
int f;//最大流
int c;//最小费用
int start,end;//源点和汇点 bool vis[MAXN];//在队列标志
int que[MAXN];
int pre[MAXN];
int dist[MAXN];//s-t路径最小耗费
bool SPFA()
{
int front=,rear=;
for(int u=;u<=n;u++)
{
if(u==start)
{
que[rear++]=u;
dist[u]=;
vis[u]=true;
}
else
{
dist[u]=INF;
vis[u]=false;
}
}
while(front!=rear)
{
int u=que[front++];
vis[u]=false;
if(front>=MAXN)front=;
for(int v=;v<=n;v++)
{
if(cap[u][v]>flow[u][v]&&dist[v]>dist[u]+cost[u][v])
{
dist[v]=dist[u]+cost[u][v];
pre[v]=u;
if(!vis[v])
{
vis[v]=true;
que[rear++]=v;
if(rear>=MAXN)rear=;
}
}
}
}
if(dist[end]>=INF)return false;
return true;
} void minCostMaxflow()
{
memset(flow,,sizeof(flow));
c=f=;
while(SPFA())
{
int Min=INF;
for(int u=end;u!=start;u=pre[u])
Min=min(Min,cap[pre[u]][u]-flow[pre[u]][u]);
for(int u=end;u!=start;u=pre[u])
{
flow[pre[u]][u]+=Min;
flow[u][pre[u]]-=Min;
}
c+=dist[end]*Min;
f+=Min;
}
}
//************************************************************ struct Node
{
int x,y;
};
Node node1[MAXN],node2[MAXN];
char str[MAXN][MAXN];
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int N,M;
while(scanf("%d%d",&N,&M))
{
if(N==&&M==)break;
int tol1=,tol2=;
for(int i=;i<N;i++)
{
scanf("%s",&str[i]);
for(int j=;j<M;j++)
{
if(str[i][j]=='m')
{
tol1++;
node1[tol1].x=i;
node1[tol1].y=j;
}
else if(str[i][j]=='H')
{
tol2++;
node2[tol2].x=i;
node2[tol2].y=j;
}
}
}
start=;
n=tol1+tol2+;
end=tol1+tol2+;
memset(cap,,sizeof(cap));
memset(cost,,sizeof(cost));
for(int i=;i<=tol1;i++)
{
cost[][i]=cost[i][]=;
cap[][i]=;
}
for(int i=;i<=tol2;i++)
{
cost[tol1+i][end]=;
cap[tol1+i][end]=;
}
for(int i=;i<=tol1;i++)
for(int j=;j<=tol2;j++)
{
cost[i][tol1+j]=abs(node1[i].x-node2[j].x)+abs(node1[i].y-node2[j].y);
cost[tol1+j][i]=-cost[i][tol1+j];
cap[i][tol1+j]=;
}
minCostMaxflow();
printf("%d\n",c);
}
return ;
}
 /*
POJ 2195 Going Home
邻接表形式最小费用最大流
*/ #include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std; const int MAXN=;
const int MAXE=;
const int INF=0x3f3f3f3f;
struct Edge
{
int from;
int to;
int next;
int re;//记录逆边的下标
int cap;//容量
int cost;//费用
}edge[MAXE];
int pre[MAXN];
int head[MAXN];
bool vis[MAXN];
int que[MAXN];
int dist[MAXN];
int tol;//边的总数
void add(int u,int v,int ca,int co)
{
edge[tol].from=u;
edge[tol].to=v;
edge[tol].cap=ca;
edge[tol].cost=co;
edge[tol].re=tol+;
edge[tol].next=head[u];
head[u]=tol++; edge[tol].from=v;//加逆边
edge[tol].to=u;
edge[tol].cap=;
edge[tol].cost=-co;
edge[tol].re=tol-;
edge[tol].next=head[v];
head[v]=tol++;
}
int n;
int start;
int end;
bool SPFA()
{
int front=,rear=;
for(int v=;v<=n;v++)
{
if(v==start)
{
que[rear++]=v;
vis[v]=true;
dist[v]=;
}
else
{
dist[v]=INF;
vis[v]=false;
}
}
while(front!=rear)
{
int u=que[front++];
vis[u]=false;
if(front>=MAXN)front=;
for(int i=head[u];i!=-;i=edge[i].next)
{
int v=edge[i].to;
if(edge[i].cap&&dist[v]>dist[u]+edge[i].cost)
{
dist[v]=dist[u]+edge[i].cost;
pre[v]=i;
if(!vis[v])
{
que[rear++]=v;
vis[v]=true;
if(rear>=MAXN)rear=;
}
}
}
}
if(dist[end]==INF)return false;
return true;
}
int c;//费用
int f;//最大流 void minCostMaxflow()
{
c=f=;
int u,p;
while(SPFA())
{
int Min=INF;
for(u=end;u!=start;u=edge[p].from)
{
p=pre[u];
Min=min(Min,edge[p].cap);
}
for(u=end;u!=start;u=edge[p].from)
{
p=pre[u];
edge[p].cap-=Min;
edge[edge[p].re].cap+=Min; }
c+=dist[end]*Min;
f+=Min;
}
} struct Node
{
int x,y;
};
Node node1[MAXN],node2[MAXN];
char str[MAXN][MAXN];
int main()
{
// freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int N,M;
while(scanf("%d%d",&N,&M))
{
if(N==&&M==)break;
int tol1=,tol2=;
for(int i=;i<N;i++)
{
scanf("%s",&str[i]);
for(int j=;j<M;j++)
{
if(str[i][j]=='m')
{
tol1++;
node1[tol1].x=i;
node1[tol1].y=j;
}
else if(str[i][j]=='H')
{
tol2++;
node2[tol2].x=i;
node2[tol2].y=j;
}
}
}
start=;
n=tol1+tol2+;
end=tol1+tol2+; //加边之前一定要初始化
tol=;//边数
memset(head,-,sizeof(head));//一定要初始化为-1 for(int i=;i<=tol1;i++)
{
add(,i,,);
}
for(int i=;i<=tol2;i++)
{
add(tol1+i,end,,);
}
for(int i=;i<=tol1;i++)
for(int j=;j<=tol2;j++)
{
int temp=abs(node1[i].x-node2[j].x)+abs(node1[i].y-node2[j].y);
add(i,tol1+j,,temp);
}
minCostMaxflow();
printf("%d\n",c);
}
return ;
}