裸的最小费用流,当然也可以用KM算法解决,但是比较难写。
注意反向边的距离为正向边的相反数(因此要用SPFA)
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<cstring> #include<algorithm> using namespace std; const int maxn=302,src=301,sink=300,INF=1e+8; int n,m,man,house,x[maxn],y[maxn],cap[maxn][maxn],w[maxn][maxn]; vector<int> next[maxn]; void addedge(int a,int b) { next[a].push_back(b);next[b].push_back(a); cap[a][b]=1;cap[b][a]=0; } int ab(int xx) { if(xx<0)return xx*(-1); else return xx; } int fa[maxn]; int len(int a,int b) { if((a==src)||(a==sink)||(b==src)||(b==sink))return 0; return ab(x[b]-x[a])+ab(y[b]-y[a]); } bool inq[maxn]; int times[maxn],dist[maxn]; bool SPFA() { memset(inq,0,sizeof(inq)); for(int i=0;i<maxn;i++) dist[i]=INF; dist[src]=0; queue<int>q; q.push(src); inq[src]=true; while(!q.empty()) { int now=q.front(); q.pop(); inq[now]=false; for(int i=0;i<next[now].size();i++) { int np=next[now][i]; if((dist[np]>dist[now]+w[now][np])&&cap[now][np]>0) { dist[np]=dist[now]+w[now][np]; fa[np]=now; if(!inq[np]){q.push(np);inq[np]=true;} } } } if( dist[sink]<INF)return true; else return false; } int augment() { int u=sink,delta=INF; while(u!=src) { if(cap[fa[u]][u]<delta)delta=cap[fa[u]][u]; u=fa[u]; } u=sink; while(u!=src) { cap[fa[u]][u]-=delta; cap[u][fa[u]]+=delta; u=fa[u]; } return dist[sink]*delta; } int main() {ios::sync_with_stdio(false); while(cin>>n>>m) { if(n==0&&m==0)return 0; man=0;house=150; memset(cap,0,sizeof(cap));memset(w,0,sizeof(w)); for(int i=0;i<maxn;i++) while(next[i].size()>0)next[i].pop_back(); for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { char c; cin>>c; if(c=='m') { x[man]=i;y[man]=j; man++; } if(c=='H') { x[house]=i;y[house]=j; house++; } } } for(int i=150;i<house;i++) { addedge(i,sink); for(int j=0;j<man;j++) addedge(j,i); } for(int j=0;j<man;j++) addedge(src,j); int ans=0; for(int i=0;i<man;i++) for(int j=150;j<house;j++) { w[i][j]=len(i,j); w[j][i]=w[i][j]*(-1); } while(SPFA()) { ans+=augment(); } cout<<ans<<endl; } return 0; }
,没什么别的了,很简单。