HDU 1533 Going Home (最小费用流)

题意:

  一个矩阵n*m,其中有k个房子和k个人,k个人分别必须走到任意一个房子中(匹配),但是权值就是长度(非欧拉距离),求匹配完的权之和。

思路:

  建图方法是,首先将k个人和k个房子分别抽出来到集合X和Y中,计算两两之间的距离,X到Y有一条边,费用为正,容量为1;Y到X也有一条边,费用为负,容量为0(其实两条边就是相反的)。添加一个源点0号到X集,添加一个汇点2*k+1号到Y集,这些费用都是0,容量都是1。

  建完图就用正常的方法(EK+SPFA)来解决就行了。由于我想要让每次增广路只找到流为1的路径,所以我在2*k+1号点后面还加个点2*k+2,仅1条边,费用为0,容量为1。这样每次spfa至多也只有1流量通过到汇点了。(可不用此法,将每次spfa所得cost[end]*flow[end]就行了,因为可能流过的并不止1流量。)

  

  1 #include <bits/stdc++.h>
  2 #define LL long long
  3 #define pii pair<int,int>
  4 #define INF 0x7f7f7f7f
  5 using namespace std;
  6 const int N=10000+20;
  7 vector<int> vect[N];
  8 int flow[N], cost[N], path[N], inq[N], edge_cnt;
  9 struct node
 10 {
 11     int from;
 12     int to;
 13     int val;
 14     int cap;
 15     int flo;
 16 }edge[N*4];//边数要自己估计,不然就会RE
 17 
 18 
 19 void add_node(int from,int to, int val, int cap, int flo)
 20 {
 21     edge[edge_cnt].from=from;
 22     edge[edge_cnt].to=to;
 23     edge[edge_cnt].val=val;
 24     edge[edge_cnt].cap=cap;
 25     edge[edge_cnt].flo=flo;
 26     vect[from].push_back(edge_cnt++);
 27 }
 28 
 29 int spfa(int s,int e)
 30 {
 31     deque<int> que;
 32     que.push_back(s);
 33     inq[s]=1;
 34     flow[s]=INF;
 35     cost[s]=0;
 36 
 37     while(!que.empty())
 38     {
 39         int x=que.front();
 40         que.pop_front();
 41         inq[x]=0;
 42         for(int i=0; i<vect[x].size(); i++)
 43         {
 44             node e=edge[vect[x][i]];
 45             if(e.cap>e.flo && cost[e.to]>cost[e.from] + e.val)
 46             {
 47                 cost[e.to]=cost[x]+ e.val;//每次的流最多只是1。
 48                 path[e.to]=vect[x][i];
 49                 flow[e.to]=min(flow[e.from], e.cap-e.flo);
 50                 if(!inq[e.to])
 51                 {
 52                     inq[e.to]=1;
 53                     que.push_back(e.to);
 54                 }
 55             }
 56         }
 57     }
 58     return cost[e];
 59 }
 60 
 61 int cal(int s,int e)
 62 {
 63     int ans=0;
 64     while(true)
 65     {
 66         memset(flow, 0, sizeof(flow));
 67         memset(cost, 0x7f, sizeof(cost));
 68         memset(path, 0, sizeof(path));
 69         memset(inq, 0, sizeof(inq));
 70 
 71         if(spfa(s,e)==INF) return ans;
 72         ans+=cost[e]; 
 73 
 74         int ed=e-1;//连e点那条边不清空,可以保证每次至多1流。
 75         while(ed!=s)
 76         {
 77             int t=path[ed];
 78             edge[t].flo++;
 79             edge[t^1].flo--;
 80             ed=edge[t].from;
 81         }
 82     }
 83 }
 84 
 85 int main()
 86 {
 87     freopen("input.txt", "r", stdin);
 88     int n, m;char ch;
 89     while(~scanf("%d%d",&n,&m),n+m)
 90     {
 91         for(int i=N-1; i>=0; i--)    vect[i].clear();
 92         memset(edge, 0, sizeof(edge));
 93         edge_cnt=0;
 94         int cnt=0;
 95         vector<pii> vect_h,vect_m;
 96 
 97         for(int i=1; i<=n; i++)
 98             for(int j=1; j<=m; j++)
 99             {
100                 cin>>ch;
101                 if(ch=='H')    vect_h.push_back(make_pair(i,j)),cnt++;
102                 if(ch=='m')    vect_m.push_back(make_pair(i,j));
103             }
104 
105 
106         for(int i=0; i<vect_m.size(); i++)
107         {
108             int a=vect_m[i].first, b=vect_m[i].second;
109             for(int j=0; j<vect_h.size(); j++)
110             {
111                 int c=vect_h[j].first, d=vect_h[j].second;
112                 int dis=abs(c-a)+abs(d-b);
113 
114                 add_node(i+1,cnt+j+1,dis,1,0);
115                 add_node(cnt+j+1,i+1,-dis,0,0);
116             }
117         }
118 
119         for(int i=0; i<vect_m.size(); i++)            //添加源点
120         {
121             add_node(0,i+1,0,1,0);
122             add_node(i+1,0,0,0,0);
123 
124         }
125         for(int j=0; j<vect_h.size(); j++)          //添加汇点
126         {
127             add_node(cnt+j+1,cnt*2+1,0,1,0);
128             add_node(cnt*2+1,cnt+j+1,0,0,0);
129         }
130         add_node(cnt*2+1,cnt*2+2, 0, 1, 0);     //这是为了每次只流过1。再汇点后面再加个汇点,容量是1,但是每次spfa后都不增加flow。
131         add_node(cnt*2+2,cnt*2+1, 0, 0, 0);
132         cout<<cal(0,cnt*2+2)<<endl;
133     }
134     return 0;
135 }
AC代码
原文地址:https://www.cnblogs.com/xcw0754/p/4644005.html