比较裸地最小费用流题,对每个人对每个房子进行连容量为1的边,然后设定源点,并向所有的人连费用为0,容量为1的边,然后设定汇点,房子向汇点连容量为1费用为0的边。然后跑最小费用流
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<queue> #include<cmath> using namespace std; const int inf=1<<29; const int maxn=1e4+100; const int maxm=2e5; struct Node { int x; int y; Node(){} Node(int sx,int sy) { x=sx; y=sy; } }P[maxn],H[110]; int e,st,des,head[maxn],nxt[maxm],cost[maxm],flow[maxm],pnt[maxm],dist[maxn],pre[maxn]; int n,m,a[110][110],ho_id,pep_id; bool vis[maxn]; queue<int> q; char map[110][110]; void AddEdge(int u,int v,int c,int f) { pnt[e]=v;nxt[e]=head[u];cost[e]=c;flow[e]=f;head[u]=e++; pnt[e]=u;nxt[e]=head[v];cost[e]=-c;flow[e]=0;head[v]=e++; } bool Spfa(int st,int des) { for(int i=st;i<=des;i++) { dist[i]=inf; pre[i]=-1; } dist[st]=0; q.push(st); while(!q.empty()) { int u=q.front(); vis[u]=0; q.pop(); for(int i=head[u];i!=-1;i=nxt[i]) if(flow[i]&&dist[pnt[i]]>dist[u]+cost[i]) { dist[pnt[i]]=dist[u]+cost[i]; pre[pnt[i]]=i; if(!vis[pnt[i]]) { vis[pnt[i]]=1; q.push(pnt[i]); } } } return dist[des]!=inf; } int mincostflow() { int ans=0; while(Spfa(st,des)) { int mini=inf; for(int i=pre[des];i!=-1;i=pre[pnt[i^1]]) mini=min(mini,flow[i]); for(int i=pre[des];i!=-1;i=pre[pnt[i^1]]) { flow[i]-=mini; flow[i^1]+=mini; } ans+=mini*dist[des]; } return ans; } void Build() { e=st=0;des=pep_id+ho_id+1; memset(head,-1,sizeof(head)); for(int i=0;i<pep_id;i++) { AddEdge(st,i+1,0,1); for(int j=0;j<ho_id;j++) AddEdge(i+1,pep_id+1+j,abs(P[i].x-H[j].x)+abs(P[i].y-H[j].y),1); } for(int i=0;i<ho_id;i++) AddEdge(pep_id+i+1,des,0,1); } void solve() { Build(); printf("%d\n",mincostflow()); } int main() { while(scanf("%d%d",&n,&m)&&(n+m)) { ho_id=pep_id=0; memset(a,0,sizeof(a)); for(int i=0;i<n;i++) scanf("%s",map[i]); for(int i=0;i<n;i++) for(int j=0;j<m;j++) { if(map[i][j]=='m') P[pep_id++]=Node(i,j); if(map[i][j]=='H') H[ho_id++]=Node(i,j); } solve(); } return 0; }