题意:
一个矩阵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代码
作者:xcw0754
水平有限,若有疏漏,欢迎指出。