http://poj.org/problem?id=2195

对km算法不理解,模板用的也不好。

下面是大神的解释。

  1. KM算法的要点是在相等子图中寻找完备匹配,其正确性的基石是:任何一个匹配的权值之和都不大于所有顶点的顶标之和,而能够取到相等的必然是最大权匹配。
  1. 左右两边点数不等时,KM算法的正确性也是可以得到保证的。原因如下:
  2. KM算法中可行点标的定义,有:
  3. 任意匹配的权值 <= 该匹配所覆盖的所有点的顶标值 <= KM算法所得到的匹配所覆盖的所有点的顶标值 = KM算法所得到的的匹配的权值
  4.  
  5. 上面的证明与网上大多数证明不同点在于,网上的证明中只是模糊地说“图中所有点的顶标值”,这只在两点集点数相等时才正确。
  6.  
  7. 上面第二个不等号最为关键,它的理由是:假定|X|<=|Y|,则任意时刻,Y集合中KM算法所取的匹配未覆盖的点的顶标必为0
  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cmath>
  4. #include <algorithm>
  5. using namespace std;
  6.  
  7. const int maxn = + ;
  8.  
  9. const int INF = 0x7fffffff;
  10.  
  11. int n,m;
  12. int W[maxn][maxn]; //存储权值
  13. int Lx[maxn], Ly[maxn]; // 顶标
  14. int left[maxn]; // left[i]为右边第i个点的匹配点编号
  15. bool S[maxn], T[maxn]; // S[i]和T[i]为左/右第i个点是否已标记
  16.  
  17. bool match(int i){
  18. S[i] = true;
  19. for(int j = ; j <= m; j++) if (Lx[i]+Ly[j]==W[i][j] && !T[j])
  20. {
  21. T[j] = true;
  22. if (!left[j] || match(left[j]))
  23. {
  24. left[j] = i;
  25. return true;
  26. }
  27. }
  28. return false;
  29. }
  30.  
  31. void update(){
  32. int a = INF;
  33. for(int i = ; i <= n; i++) if(S[i])
  34. for(int j = ; j <= m; j++) if(!T[j])
  35. a = min(a, W[i][j]-Lx[i]-Ly[j]); //若是最大权匹配则Lx[i]+Ly[j]-W[i][j]; 同样是取最小值
  36. for(int i = ; i <= n; i++) {
  37. if(S[i]) Lx[i] -= a;
  38. if(T[i]) Ly[i] += a;
  39. }
  40. }
  41.  
  42. void KM() {
  43. for(int i = ; i <= n; i++) {
  44. left[i] = Lx[i] = Ly[i] = ;
  45. for(int j = ; j <= m; j++)
  46. Lx[i] = min(Lx[i], W[i][j]); //若是最大权匹配.则初始值顶标取最大值,最小匹配则去最小值
  47. }
  48. for(int i = ; i <= n; i++) {
  49. for(;;) {
  50. for(int j = ; j <= m; j++) S[j] = T[j] = ;
  51. if(match(i)) break; else update();
  52. }
  53. }
  54. }
  55. char s[];
  56. struct point
  57. {
  58. int x,y;
  59. };
  60. point man[],house[];
  61. int main(){
  62. //freopen("a.txt","r",stdin);
  63. int sum,man_num,house_num;
  64. while(scanf("%d%d",&n,&m)!=EOF&&(n+m))
  65. {
  66. int i,j;
  67. man_num=,house_num=;
  68. for(int i=;i<=n;i++)
  69. {
  70. scanf("%s",s+);
  71. for(int j=;j<=m;j++)
  72. {
  73. if(s[j]=='m')
  74. {
  75. man[man_num].x=i;
  76. man[man_num++].y=j;
  77. }
  78. else if(s[j]=='H')
  79. {
  80. house[house_num].x=i;
  81. house[house_num++].y=j;
  82. }
  83. }
  84. }
  85. man_num--,house_num--;
  86. // printf("%d %d\n",man_num,house_num);
  87. n=man_num;m=house_num;
  88. for(int i=;i<=man_num;i++)
  89. for(int j=;j<=house_num;j++)
  90. W[i][j]=abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y);
  91. sum=;
  92. KM(); // 最大权匹配
  93. for(i=;i<=m;i++)
  94. sum+=W[left[i]][i];
  95. printf("%d\n",sum);
  96. }
  97. //for(int i = 1; i <= n; i++) printf("left[%d]=%d\n", i,left[i]);
  98. //getch();
  99. return ;
  100. }

当然还可以用费用流来做.

算出每一个到每一个房子的距离后建图。
源点与人连,容量1,费用0
人与每个房子都要连,容量1,费用为距离
每个房子与汇点连,容量1,费用0
求一次最小费用即可
另外此题数据的范围开大点。
Bellmanford:
  1. #include<cstdio>
  2. #include<cstring>
  3. #include<queue>
  4. #include<vector>
  5. #include<algorithm>
  6. #include<cassert>
  7. using namespace std;
  8.  
  9. const int maxn = + ;
  10. const int INF = ;
  11.  
  12. struct Edge
  13. {
  14. int from, to, cap, flow, cost;
  15. Edge(int u, int v, int c, int f, int w):from(u),to(v),cap(c),flow(f),cost(w) {}
  16. };
  17.  
  18. struct MCMF
  19. {
  20. int n, m;
  21. vector<Edge> edges;
  22. vector<int> G[maxn];
  23. int inq[maxn]; // 是否在队列中
  24. int d[maxn]; // Bellman-Ford
  25. int p[maxn]; // 上一条弧
  26. int a[maxn]; // 可改进量
  27.  
  28. void init(int n)
  29. {
  30. this->n = n;
  31. for(int i = ; i < n; i++) G[i].clear();
  32. edges.clear();
  33. }
  34.  
  35. void AddEdge(int from, int to, int cap, int cost)
  36. {
  37. edges.push_back(Edge(from, to, cap, , cost));
  38. edges.push_back(Edge(to, from, , , -cost));
  39. m = edges.size();
  40. G[from].push_back(m-);
  41. G[to].push_back(m-);
  42. }
  43.  
  44. bool BellmanFord(int s, int t, int flow_limit, int& flow, int& cost)
  45. {
  46. for(int i = ; i < n; i++) d[i] = INF;
  47. memset(inq, , sizeof(inq));
  48. d[s] = ; inq[s] = ; p[s] = ; a[s] = INF;
  49.  
  50. queue<int> Q;
  51. Q.push(s);
  52. while(!Q.empty())
  53. {
  54. int u = Q.front(); Q.pop();
  55. inq[u] = ;
  56. for(int i = ; i < G[u].size(); i++)
  57. {
  58. Edge& e = edges[G[u][i]];
  59. if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
  60. {
  61. d[e.to] = d[u] + e.cost;
  62. p[e.to] = G[u][i];
  63. a[e.to] = min(a[u], e.cap - e.flow);
  64. if(!inq[e.to]) { Q.push(e.to); inq[e.to] = ; }
  65. }
  66. }
  67. }
  68. if(d[t] == INF) return false;
  69. if(flow + a[t] > flow_limit) a[t] = flow_limit - flow;
  70. flow += a[t];
  71. cost += d[t] * a[t];
  72. for(int u = t; u != s; u = edges[p[u]].from)
  73. {
  74. edges[p[u]].flow += a[t];
  75. edges[p[u]^].flow -= a[t];
  76. }
  77. return true;
  78. }
  79.  
  80. // 需要保证初始网络中没有负权圈
  81. int MincostFlow(int s, int t, int flow_limit, int& cost)
  82. {
  83. int flow = ; cost = ;
  84. while(flow < flow_limit && BellmanFord(s, t, flow_limit, flow, cost));
  85. return flow;
  86. }
  87.  
  88. };
  89.  
  90. MCMF g;
  91. typedef struct{
  92. int x,y;
  93. }Point;
  94.  
  95. Point man[maxn],home[maxn];
  96. int man_sum,home_sum;
  97.  
  98. int main()
  99. {
  100. int n,m,i,j;
  101. char s[maxn];
  102. while(~scanf("%d%d",&n,&m) && n || m)
  103. {
  104. getchar();
  105. man_sum=home_sum=;
  106. for(i=;i<=n;i++)
  107. { //记录下人与房子的坐标
  108. gets(s);
  109. for(j=;j<m;j++)
  110. {
  111. if(s[j]=='m')
  112. {
  113. man[man_sum].x=i;
  114. man[man_sum++].y=j+;
  115. }
  116. if(s[j]=='H')
  117. {
  118. home[home_sum].x=i;
  119. home[home_sum++].y=j+;
  120. }
  121. }
  122. }
  123. man_sum--; home_sum--;
  124. g.init(man_sum+home_sum+);
  125.  
  126. for(i=;i<=man_sum;i++) g.AddEdge(, i, , ); //源点指向人
  127.  
  128. for(i=;i<=man_sum;i++) //人指向房子
  129. {
  130. for(j=man_sum+;j<=man_sum+home_sum;j++)
  131. {
  132. g.AddEdge(i, j, , abs(man[i].x-home[j-man_sum].x)+abs(man[i].y-home[j-man_sum].y));
  133. }
  134. }
  135.  
  136. for(i=man_sum+;i<=man_sum+home_sum;i++) g.AddEdge(i, man_sum+home_sum+, , ); //房子指向汇点
  137.  
  138. int cost;
  139. g.MincostFlow(, man_sum+home_sum+, man_sum, cost);
  140. printf("%d\n", cost);
  141. }
  142. return ;
  143. }

SPFA:

参考:http://blog.csdn.net/lenleaves/article/details/7904588

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<string>
  5. #include<queue>
  6. #include<cmath>
  7. #include<cstdio>
  8.  
  9. using namespace std;
  10.  
  11. #define MAXN 2000
  12. #define MAXM 110000
  13. #define INF 0x3FFFFFF
  14.  
  15. struct edge
  16. {
  17. int to,c,w,next;
  18. };
  19.  
  20. edge e[MAXM];
  21. bool in[MAXN];
  22. int hx[],hy[],mx[],my[];
  23. int head[MAXN],dis[MAXN],pre[MAXN],en,maxflow,mincost;
  24. int vn,st,ed,r,c,hn,mn;
  25.  
  26. void add(int a,int b,int c,int d)
  27. {
  28. e[en].to=b;
  29. e[en].c=c;
  30. e[en].w=d;
  31. e[en].next=head[a];
  32. head[a]=en++;
  33. e[en].to=a;
  34. e[en].c=;
  35. e[en].w=-d;
  36. e[en].next=head[b];
  37. head[b]=en++;
  38. }
  39.  
  40. bool spfa(int s)
  41. {
  42. queue<int> q;
  43. for(int i=;i<=vn;i++)
  44. {
  45. dis[i]=INF;
  46. in[i]=false;
  47. pre[i]=-;
  48. }
  49. dis[s]=;
  50. in[s]=true;
  51. q.push(s);
  52. while(!q.empty())
  53. {
  54. int tag=q.front();
  55. in[tag]=false;
  56. q.pop();
  57. for(int i=head[tag];i!=-;i=e[i].next)
  58. {
  59. int j=e[i].to;
  60. if(e[i].w+dis[tag]<dis[j] && e[i].c)
  61. {
  62. dis[j]=e[i].w+dis[tag];
  63. pre[j]=i;
  64. if(!in[j])
  65. {
  66. q.push(j);
  67. in[j]=true;
  68. }
  69. }
  70. }
  71. }
  72. if(dis[ed]==INF)
  73. return false;
  74. return true;
  75. }
  76.  
  77. void update()
  78. {
  79. int flow=INF;
  80. for (int i=pre[ed];i!=-;i=pre[e[i^].to])
  81. if(e[i].c<flow) flow=e[i].c;
  82. for (int i=pre[ed];i!=-;i=pre[e[i^].to])
  83. {
  84. e[i].c-=flow,e[i^].c+=flow;
  85. }
  86. maxflow+=flow;
  87. mincost+=flow*dis[ed];
  88. }
  89.  
  90. void mincostmaxflow()
  91. {
  92. maxflow=,mincost=;
  93. while(spfa(st))
  94. update();
  95. }
  96.  
  97. void solve()
  98. {
  99. char s[];
  100. hn=,mn=,en=;
  101. memset(head,-,sizeof(head));
  102. for(int i=;i<=r;i++)
  103. {
  104. scanf("%s",s);
  105. for(int j=;j<c;j++)
  106. {
  107. if(s[j]=='m') mx[mn]=i,my[mn++]=j+;
  108. if(s[j]=='H') hx[hn]=i,hy[hn++]=j+;
  109. }
  110. }
  111. vn=mn+hn+;
  112. st=,ed=mn+hn+;
  113. for(int i=;i<mn;i++)
  114. for(int j=;j<hn;j++)
  115. {
  116. int dist=abs(mx[i]-hx[j])+abs(my[i]-hy[j]);
  117. add(i+,j++mn,,dist);
  118. }
  119. for(int i=;i<mn;i++)
  120. add(st,i+,,);
  121. for(int i=;i<hn;i++)
  122. add(i++mn,ed,,);
  123. mincostmaxflow();
  124. printf("%d\n",mincost);
  125. }
  126.  
  127. int main()
  128. {
  129. while(scanf("%d%d",&r,&c)!=EOF && r+c)
  130. solve();
  131. return ;
  132. }

poj - 2195 Going Home (费用流 || 最佳匹配)的更多相关文章

  1. POJ 2195 Going Home (费用流)

    题面 On a grid map there are n little men and n houses. In each unit time, every little man can move o ...

  2. POJ 2175 Evacuation Plan (费用流,负环,消圈法,SPFA)

    http://poj.org/problem?id=2175 Evacuation Plan Time Limit: 1000MS   Memory Limit: 65536K Total Submi ...

  3. POJ 2516 Minimum Cost (费用流)

    题面 Dearboy, a goods victualer, now comes to a big problem, and he needs your help. In his sale area ...

  4. Going Home POJ - 2195 (最小费用最大流)

    On a grid map there are n little men and n houses. In each unit time, every little man can move one ...

  5. POJ 3680 Intervals(费用流)

    Intervals Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 5762   Accepted: 2288 Descrip ...

  6. POJ 2175 Evacuation Plan 费用流 负圈定理

    题目给了一个满足最大流的残量网络,判断是否费用最小. 如果残量网络中存在费用负圈,那么不是最优,在这个圈上增广,增广1的流量就行了. 1.SPFA中某个点入队超过n次,说明存在负环,但是这个点不一定在 ...

  7. POJ 3680 Intervals(费用流+负权优化)

    [题目链接] http://poj.org/problem?id=3680 [题目大意] 有N个带权重的区间,现在要从中选取一些区间, 要求任意点都不被超过K个区间所覆盖,请最大化总的区间权重. [题 ...

  8. poj 2135 Farm Tour 费用流

    题目链接 给一个图, N个点, m条边, 每条边有权值, 从1走到n, 然后从n走到1, 一条路不能走两次,求最短路径. 如果(u, v)之间有边, 那么加边(u, v, 1, val), (v, u ...

  9. BZOJ3502PA2012Tanie linie&BZOJ2288[POJ Challenge]生日礼物——模拟费用流+链表+堆

    题目描述 n个数字,求不相交的总和最大的最多k个连续子序列. 1<= k<= N<= 1000000. 输入 输出 样例输入 5 2 7 -3 4 -9 5 样例输出 13   根据 ...

随机推荐

  1. 移动端UI自动化Appium测试——Appium server两种启动方式

    执行自动化测试之前,需要先运行appium server,这样才能形成server与java client的通信,启动server有两种方式,一种是命令,一种是按钮图标,具体使用如下: 1.用命令启动 ...

  2. Partial(部分方法,局部方法),virtual(虚方法),abstract(抽象方法)

    Partial 部分方法顾明思议是方法的一部分,不完整的,在ide编译时候,会将所有部分方法加载到一起统一编译,如果分部方法没有被实现,编译器就不会.对他们进行编译. 局部类型的限制 (1) 局部类型 ...

  3. JDK集合框架--LinkedList

    上一篇讲了ArrayList,它有一个"孪生兄弟"--LinkedList,这两个集合类总是经常会被拿来比较,今天就分析一下LinkedList,然后总结一下这俩集合类的不同 首先 ...

  4. 抽象工厂模式和php实现

    抽象工厂模式: 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类.抽象工厂模式又称为Kit模式,属于对象创建型模式. ...

  5. 微信小程序button授权页面,用户拒绝后仍可再次授权

    微信小程序授权页面,进入小程序如果没授权跳转到授权页面,授权后跳转到首页,如果用户点拒绝下次进入小程序还是能跳转到授权页面,授权页面如下 app.js  中的 onLaunch或onShow中加如下代 ...

  6. 掌握Spark机器学习库-07.6-线性回归实现房价预测

    数据集 house.csv 数据概览 代码 package org.apache.spark.examples.examplesforml import org.apache.spark.ml.fea ...

  7. addslashes,stripslashes

    官方介绍: (PHP 4, PHP 5) addslashes — 使用反斜线引用字符串 返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线.这些字符是单引号(’).双引号(”). ...

  8. 前端Unicode转码的好处

    站长工具支持Unicode转码:http://tool.chinaz.com/Tools/Unicode.aspx (这是一个网页标题)转码后 ------>变为:\u8fd9\u662f\u4 ...

  9. [分享] IMX6嵌入式开发板linux QT挂载U盘及TF卡

    本文转自迅为开发板:http://www.topeetboard.com 开发平台:iMX6开发板 linux QT 系统下挂载 u 盘如下图所示,qt 启动之后,在超级终端中使用命令“mknod / ...

  10. confluence的安装

    参考链接:https://www.ilanni.com/?p=11989 一.什么是confluence confluence是一个专业的企业知识管理与协同软件,可以用于构建企业wiki.通过它可以实 ...