poj - 2195 Going Home (费用流 || 最佳匹配)
http://poj.org/problem?id=2195
对km算法不理解,模板用的也不好。
下面是大神的解释。
- KM算法的要点是在相等子图中寻找完备匹配,其正确性的基石是:任何一个匹配的权值之和都不大于所有顶点的顶标之和,而能够取到相等的必然是最大权匹配。
- 左右两边点数不等时,KM算法的正确性也是可以得到保证的。原因如下:
- 由KM算法中可行点标的定义,有:
- 任意匹配的权值 <= 该匹配所覆盖的所有点的顶标值 <= KM算法所得到的匹配所覆盖的所有点的顶标值 = KM算法所得到的的匹配的权值
- 上面的证明与网上大多数证明不同点在于,网上的证明中只是模糊地说“图中所有点的顶标值”,这只在两点集点数相等时才正确。
- 上面第二个不等号最为关键,它的理由是:假定|X|<=|Y|,则任意时刻,Y集合中KM算法所取的匹配未覆盖的点的顶标必为0!
- #include <cstdio>
- #include <cstring>
- #include <cmath>
- #include <algorithm>
- using namespace std;
- const int maxn = + ;
- const int INF = 0x7fffffff;
- int n,m;
- int W[maxn][maxn]; //存储权值
- int Lx[maxn], Ly[maxn]; // 顶标
- int left[maxn]; // left[i]为右边第i个点的匹配点编号
- bool S[maxn], T[maxn]; // S[i]和T[i]为左/右第i个点是否已标记
- bool match(int i){
- S[i] = true;
- for(int j = ; j <= m; j++) if (Lx[i]+Ly[j]==W[i][j] && !T[j])
- {
- T[j] = true;
- if (!left[j] || match(left[j]))
- {
- left[j] = i;
- return true;
- }
- }
- return false;
- }
- void update(){
- int a = INF;
- for(int i = ; i <= n; i++) if(S[i])
- for(int j = ; j <= m; j++) if(!T[j])
- a = min(a, W[i][j]-Lx[i]-Ly[j]); //若是最大权匹配则Lx[i]+Ly[j]-W[i][j]; 同样是取最小值
- for(int i = ; i <= n; i++) {
- if(S[i]) Lx[i] -= a;
- if(T[i]) Ly[i] += a;
- }
- }
- void KM() {
- for(int i = ; i <= n; i++) {
- left[i] = Lx[i] = Ly[i] = ;
- for(int j = ; j <= m; j++)
- Lx[i] = min(Lx[i], W[i][j]); //若是最大权匹配.则初始值顶标取最大值,最小匹配则去最小值
- }
- for(int i = ; i <= n; i++) {
- for(;;) {
- for(int j = ; j <= m; j++) S[j] = T[j] = ;
- if(match(i)) break; else update();
- }
- }
- }
- char s[];
- struct point
- {
- int x,y;
- };
- point man[],house[];
- int main(){
- //freopen("a.txt","r",stdin);
- int sum,man_num,house_num;
- while(scanf("%d%d",&n,&m)!=EOF&&(n+m))
- {
- int i,j;
- man_num=,house_num=;
- for(int i=;i<=n;i++)
- {
- scanf("%s",s+);
- for(int j=;j<=m;j++)
- {
- if(s[j]=='m')
- {
- man[man_num].x=i;
- man[man_num++].y=j;
- }
- else if(s[j]=='H')
- {
- house[house_num].x=i;
- house[house_num++].y=j;
- }
- }
- }
- man_num--,house_num--;
- // printf("%d %d\n",man_num,house_num);
- n=man_num;m=house_num;
- for(int i=;i<=man_num;i++)
- for(int j=;j<=house_num;j++)
- W[i][j]=abs(man[i].x-house[j].x)+abs(man[i].y-house[j].y);
- sum=;
- KM(); // 最大权匹配
- for(i=;i<=m;i++)
- sum+=W[left[i]][i];
- printf("%d\n",sum);
- }
- //for(int i = 1; i <= n; i++) printf("left[%d]=%d\n", i,left[i]);
- //getch();
- return ;
- }
当然还可以用费用流来做.
- #include<cstdio>
- #include<cstring>
- #include<queue>
- #include<vector>
- #include<algorithm>
- #include<cassert>
- using namespace std;
- const int maxn = + ;
- const int INF = ;
- struct Edge
- {
- int from, to, cap, flow, cost;
- Edge(int u, int v, int c, int f, int w):from(u),to(v),cap(c),flow(f),cost(w) {}
- };
- struct MCMF
- {
- int n, m;
- vector<Edge> edges;
- vector<int> G[maxn];
- int inq[maxn]; // 是否在队列中
- int d[maxn]; // Bellman-Ford
- int p[maxn]; // 上一条弧
- int a[maxn]; // 可改进量
- void init(int n)
- {
- this->n = n;
- for(int i = ; i < n; i++) G[i].clear();
- edges.clear();
- }
- void AddEdge(int from, int to, int cap, int cost)
- {
- edges.push_back(Edge(from, to, cap, , cost));
- edges.push_back(Edge(to, from, , , -cost));
- m = edges.size();
- G[from].push_back(m-);
- G[to].push_back(m-);
- }
- bool BellmanFord(int s, int t, int flow_limit, int& flow, int& cost)
- {
- for(int i = ; i < n; i++) d[i] = INF;
- memset(inq, , sizeof(inq));
- d[s] = ; inq[s] = ; p[s] = ; a[s] = INF;
- queue<int> Q;
- Q.push(s);
- while(!Q.empty())
- {
- int u = Q.front(); Q.pop();
- inq[u] = ;
- for(int i = ; i < G[u].size(); i++)
- {
- Edge& e = edges[G[u][i]];
- if(e.cap > e.flow && d[e.to] > d[u] + e.cost)
- {
- d[e.to] = d[u] + e.cost;
- p[e.to] = G[u][i];
- a[e.to] = min(a[u], e.cap - e.flow);
- if(!inq[e.to]) { Q.push(e.to); inq[e.to] = ; }
- }
- }
- }
- if(d[t] == INF) return false;
- if(flow + a[t] > flow_limit) a[t] = flow_limit - flow;
- flow += a[t];
- cost += d[t] * a[t];
- for(int u = t; u != s; u = edges[p[u]].from)
- {
- edges[p[u]].flow += a[t];
- edges[p[u]^].flow -= a[t];
- }
- return true;
- }
- // 需要保证初始网络中没有负权圈
- int MincostFlow(int s, int t, int flow_limit, int& cost)
- {
- int flow = ; cost = ;
- while(flow < flow_limit && BellmanFord(s, t, flow_limit, flow, cost));
- return flow;
- }
- };
- MCMF g;
- typedef struct{
- int x,y;
- }Point;
- Point man[maxn],home[maxn];
- int man_sum,home_sum;
- int main()
- {
- int n,m,i,j;
- char s[maxn];
- while(~scanf("%d%d",&n,&m) && n || m)
- {
- getchar();
- man_sum=home_sum=;
- for(i=;i<=n;i++)
- { //记录下人与房子的坐标
- gets(s);
- for(j=;j<m;j++)
- {
- if(s[j]=='m')
- {
- man[man_sum].x=i;
- man[man_sum++].y=j+;
- }
- if(s[j]=='H')
- {
- home[home_sum].x=i;
- home[home_sum++].y=j+;
- }
- }
- }
- man_sum--; home_sum--;
- g.init(man_sum+home_sum+);
- for(i=;i<=man_sum;i++) g.AddEdge(, i, , ); //源点指向人
- for(i=;i<=man_sum;i++) //人指向房子
- {
- for(j=man_sum+;j<=man_sum+home_sum;j++)
- {
- g.AddEdge(i, j, , abs(man[i].x-home[j-man_sum].x)+abs(man[i].y-home[j-man_sum].y));
- }
- }
- for(i=man_sum+;i<=man_sum+home_sum;i++) g.AddEdge(i, man_sum+home_sum+, , ); //房子指向汇点
- int cost;
- g.MincostFlow(, man_sum+home_sum+, man_sum, cost);
- printf("%d\n", cost);
- }
- return ;
- }
SPFA:
参考:http://blog.csdn.net/lenleaves/article/details/7904588
- #include<iostream>
- #include<algorithm>
- #include<cstring>
- #include<string>
- #include<queue>
- #include<cmath>
- #include<cstdio>
- using namespace std;
- #define MAXN 2000
- #define MAXM 110000
- #define INF 0x3FFFFFF
- struct edge
- {
- int to,c,w,next;
- };
- edge e[MAXM];
- bool in[MAXN];
- int hx[],hy[],mx[],my[];
- int head[MAXN],dis[MAXN],pre[MAXN],en,maxflow,mincost;
- int vn,st,ed,r,c,hn,mn;
- void add(int a,int b,int c,int d)
- {
- e[en].to=b;
- e[en].c=c;
- e[en].w=d;
- e[en].next=head[a];
- head[a]=en++;
- e[en].to=a;
- e[en].c=;
- e[en].w=-d;
- e[en].next=head[b];
- head[b]=en++;
- }
- bool spfa(int s)
- {
- queue<int> q;
- for(int i=;i<=vn;i++)
- {
- dis[i]=INF;
- in[i]=false;
- pre[i]=-;
- }
- dis[s]=;
- in[s]=true;
- q.push(s);
- while(!q.empty())
- {
- int tag=q.front();
- in[tag]=false;
- q.pop();
- for(int i=head[tag];i!=-;i=e[i].next)
- {
- int j=e[i].to;
- if(e[i].w+dis[tag]<dis[j] && e[i].c)
- {
- dis[j]=e[i].w+dis[tag];
- pre[j]=i;
- if(!in[j])
- {
- q.push(j);
- in[j]=true;
- }
- }
- }
- }
- if(dis[ed]==INF)
- return false;
- return true;
- }
- void update()
- {
- int flow=INF;
- for (int i=pre[ed];i!=-;i=pre[e[i^].to])
- if(e[i].c<flow) flow=e[i].c;
- for (int i=pre[ed];i!=-;i=pre[e[i^].to])
- {
- e[i].c-=flow,e[i^].c+=flow;
- }
- maxflow+=flow;
- mincost+=flow*dis[ed];
- }
- void mincostmaxflow()
- {
- maxflow=,mincost=;
- while(spfa(st))
- update();
- }
- void solve()
- {
- char s[];
- hn=,mn=,en=;
- memset(head,-,sizeof(head));
- for(int i=;i<=r;i++)
- {
- scanf("%s",s);
- for(int j=;j<c;j++)
- {
- if(s[j]=='m') mx[mn]=i,my[mn++]=j+;
- if(s[j]=='H') hx[hn]=i,hy[hn++]=j+;
- }
- }
- vn=mn+hn+;
- st=,ed=mn+hn+;
- for(int i=;i<mn;i++)
- for(int j=;j<hn;j++)
- {
- int dist=abs(mx[i]-hx[j])+abs(my[i]-hy[j]);
- add(i+,j++mn,,dist);
- }
- for(int i=;i<mn;i++)
- add(st,i+,,);
- for(int i=;i<hn;i++)
- add(i++mn,ed,,);
- mincostmaxflow();
- printf("%d\n",mincost);
- }
- int main()
- {
- while(scanf("%d%d",&r,&c)!=EOF && r+c)
- solve();
- return ;
- }
poj - 2195 Going Home (费用流 || 最佳匹配)的更多相关文章
- 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 ...
- POJ 2175 Evacuation Plan (费用流,负环,消圈法,SPFA)
http://poj.org/problem?id=2175 Evacuation Plan Time Limit: 1000MS Memory Limit: 65536K Total Submi ...
- POJ 2516 Minimum Cost (费用流)
题面 Dearboy, a goods victualer, now comes to a big problem, and he needs your help. In his sale area ...
- 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 ...
- POJ 3680 Intervals(费用流)
Intervals Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 5762 Accepted: 2288 Descrip ...
- POJ 2175 Evacuation Plan 费用流 负圈定理
题目给了一个满足最大流的残量网络,判断是否费用最小. 如果残量网络中存在费用负圈,那么不是最优,在这个圈上增广,增广1的流量就行了. 1.SPFA中某个点入队超过n次,说明存在负环,但是这个点不一定在 ...
- POJ 3680 Intervals(费用流+负权优化)
[题目链接] http://poj.org/problem?id=3680 [题目大意] 有N个带权重的区间,现在要从中选取一些区间, 要求任意点都不被超过K个区间所覆盖,请最大化总的区间权重. [题 ...
- poj 2135 Farm Tour 费用流
题目链接 给一个图, N个点, m条边, 每条边有权值, 从1走到n, 然后从n走到1, 一条路不能走两次,求最短路径. 如果(u, v)之间有边, 那么加边(u, v, 1, val), (v, u ...
- BZOJ3502PA2012Tanie linie&BZOJ2288[POJ Challenge]生日礼物——模拟费用流+链表+堆
题目描述 n个数字,求不相交的总和最大的最多k个连续子序列. 1<= k<= N<= 1000000. 输入 输出 样例输入 5 2 7 -3 4 -9 5 样例输出 13 根据 ...
随机推荐
- 移动端UI自动化Appium测试——Appium server两种启动方式
执行自动化测试之前,需要先运行appium server,这样才能形成server与java client的通信,启动server有两种方式,一种是命令,一种是按钮图标,具体使用如下: 1.用命令启动 ...
- Partial(部分方法,局部方法),virtual(虚方法),abstract(抽象方法)
Partial 部分方法顾明思议是方法的一部分,不完整的,在ide编译时候,会将所有部分方法加载到一起统一编译,如果分部方法没有被实现,编译器就不会.对他们进行编译. 局部类型的限制 (1) 局部类型 ...
- JDK集合框架--LinkedList
上一篇讲了ArrayList,它有一个"孪生兄弟"--LinkedList,这两个集合类总是经常会被拿来比较,今天就分析一下LinkedList,然后总结一下这俩集合类的不同 首先 ...
- 抽象工厂模式和php实现
抽象工厂模式: 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类.抽象工厂模式又称为Kit模式,属于对象创建型模式. ...
- 微信小程序button授权页面,用户拒绝后仍可再次授权
微信小程序授权页面,进入小程序如果没授权跳转到授权页面,授权后跳转到首页,如果用户点拒绝下次进入小程序还是能跳转到授权页面,授权页面如下 app.js 中的 onLaunch或onShow中加如下代 ...
- 掌握Spark机器学习库-07.6-线性回归实现房价预测
数据集 house.csv 数据概览 代码 package org.apache.spark.examples.examplesforml import org.apache.spark.ml.fea ...
- addslashes,stripslashes
官方介绍: (PHP 4, PHP 5) addslashes — 使用反斜线引用字符串 返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线.这些字符是单引号(’).双引号(”). ...
- 前端Unicode转码的好处
站长工具支持Unicode转码:http://tool.chinaz.com/Tools/Unicode.aspx (这是一个网页标题)转码后 ------>变为:\u8fd9\u662f\u4 ...
- [分享] IMX6嵌入式开发板linux QT挂载U盘及TF卡
本文转自迅为开发板:http://www.topeetboard.com 开发平台:iMX6开发板 linux QT 系统下挂载 u 盘如下图所示,qt 启动之后,在超级终端中使用命令“mknod / ...
- confluence的安装
参考链接:https://www.ilanni.com/?p=11989 一.什么是confluence confluence是一个专业的企业知识管理与协同软件,可以用于构建企业wiki.通过它可以实 ...