【玲珑杯 1047】【二分匹配 KM算法或者费用流】Best couple【定义男女生的距离为最短距离,求匹配之后使得总距离最大】
来源:互联网 发布:java 变量类型 编辑:程序博客网 时间:2024/06/05 06:29
传送门:http://www.ifrog.cc/acm/problem/1055
思路:
跑一发floyd,然后再用km。
但是问题来了,这个有可能n != m。那怎么办?
其实可以补上一些不存在的点。来使得n = m。他们的权值就设置为0就好了。意思就是这些人的搭配,是对答案没有贡献的。注意不能设置为-inf。因为补上的那些点也是必须要选人的,只不过他们选了人,相当于没选而已(权值不存在。)如果设置为-inf的那些,那么他们就会把答案改了。
还有一个小trick的就是,一开始,我是把本来地图上的-1的那些点,更改为inf的,inf表示不连通,那么直接floyd就可以了不用特判那么多。那么问题又来了。如果跑了floyd后,还是不连通,那怎么办?他们的权值可是inf啊。组成新图的时候,同时也是需要把他们的权值设置为0的,也就是相当于没选。
或者直接跑费用流省去建虚点的麻烦
下面给出一些概念:
1.通俗地说,完全匹配只要一边的点都在匹配中就行。而完美匹配则是相当于要“双向的完全匹配”。
2.KM算法求的是完美匹配中权最大的一个,在本来图就达不到完美匹配,而只有若干完备匹配的情况下,KM不能求出完备匹配中权最大的一个。
3.如果本来图就达不到完美匹配,但仍要求出最大权匹配,那么该怎么办?
(1)建虚点虚边,只要把所有没有的边设成0就行了,然后跑KM算法
(2)用费用流来跑,这种情况用费用来跑相对来说代码少点,但是效率比KM稍慢
代码一:
(费用流 266ms)
#include <bits/stdc++.h>using namespace std;//最小费用最大流,求最大费用只需要取相反数结果即可。//点的总数为 N,点的编号 0~N const int maxn=205;const int maxm=100005;const int inf=0x3f3f3f3f;struct Edge{ int to,next,cap,flow,cost;}es[maxm];int head[maxn],tol;int p[maxn];//记录增广路径上 到达点i的边的编号 int d[maxn];//单位总费用bool vis[maxn];int N;//节点总个数int dp[maxn][maxn];int n, m;void init(int n){ N=n; tol=0; memset(head, -1, sizeof(head));}void addedge(int u,int v,int cap,int cost){ es[tol].to=v; es[tol].cap=cap; es[tol].cost=cost; es[tol].flow=0; es[tol].next=head[u]; head[u]=tol++; es[tol].to=u; es[tol].cap=0; es[tol].cost=-cost; es[tol].flow=0; es[tol].next=head[v]; head[v]=tol++;}bool spfa(int s,int t){//寻找花销最少的路径 //跑一遍SPFA 找s——t的最少花销路径 且该路径上每一条边不能满流 //若存在 说明可以继续增广,反之不能 queue<int>q; for(int i=0; i<=N; i++){ d[i]=inf; vis[i]=false; p[i]=-1; } d[s]=0; vis[s]=true; q.push(s); while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=false; for(int i=head[u]; i!=-1; i=es[i].next){ //逆向枚举以u为起点的边 int v=es[i].to; if(es[i].cap>es[i].flow && d[v]>d[u]+es[i].cost){//可以松弛 且 没有满流 d[v]=d[u]+es[i].cost; p[v]=i; //记录前驱边 的编号 if(!vis[v]){ vis[v]=true; q.push(v);} } } } return p[t]!=-1;//可达返回true}//返回的是最大流,cost存的是最小费用int MCMF(int s,int t,int &cost){ int flow = 0;cost = 0; while(spfa(s,t)){//每次寻找花销最小的路径 int Min=inf; //通过反向弧 在源点到汇点的最少花费路径 找最小增广流 for(int i=p[t]; i!=-1; i=p[es[i^1].to]){ if(Min>es[i].cap-es[i].flow) Min=es[i].cap-es[i].flow; } //增广 for(int i=p[t]; i!=-1; i=p[es[i^1].to]){ es[i].flow+=Min; es[i^1].flow-=Min; cost+=es[i].cost*Min;//增广流的花销 } flow+=Min;//总流量累加 } return flow;}void floyd(){ for(int k=0; k<n+m; k++) for(int i=0; i<n+m; i++) for(int j=0; j<n+m; j++) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);}int main(){ int T; scanf("%d", &T); while(T--){ scanf("%d%d", &n, &m); for(int i=0; i<n+m; i++) for(int j=0; j<n+m; j++){ scanf("%d", &dp[i][j]); if(dp[i][j] == -1)dp[i][j] = inf; } floyd(); init(n+m+2); for(int i=0; i<n; i++) addedge(n+m, i, 1, 0);//源点与男生之间建边 for(int i=n; i<n+m; i++) addedge(i, n+m+1, 1, 0);//汇点与女生之间建边 for(int i=0; i<n; i++) for(int j=n; j<n+m; j++) if(dp[i][j] < inf) addedge(i, j, 1, -dp[i][j]);//男生与女生之间距离小于inf建边 int cost; MCMF(n+m, n+m+1, cost); printf("%d\n", -cost); } return 0;}
代码二:
(KM 126ms)
#include <bits/stdc++.h>using namespace std;#define mst(ss,b) memset(ss,b,sizeof(ss));/* KM算法* 复杂度O(nx*nx*ny)* 求最大权匹配* 若求最小权匹配,可将权值取相反数,结果取相反数* 点的编号从1开始*/const int N=105;const int inf=0x3f3f3f3f;int nx,ny;//两边的点数int g[N][N];//二分图描述int link[N],lx[N],ly[N];//y中各点匹配状态,x,y中的顶点标号int slack[N];bool visx[N],visy[N];int dp[2*N][2*N];int n, m;bool dfs(int x){ visx[x]=true; for(int y=1; y<=ny; y++){ if(visy[y])continue; int tmp=lx[x]+ly[y]-g[x][y]; if(tmp==0){ visy[y]=true; if(link[y]==-1 || dfs(link[y])){ link[y]=x; return true; } } else if(slack[y]>tmp) slack[y]=tmp; } return false;}int KM(){ memset(link, -1, sizeof(link)); memset(ly, 0, sizeof(ly)); for(int i=1; i<=nx; i++){ lx[i]=-inf; for(int j=1; j<=ny; j++) if(g[i][j]>lx[i]) lx[i]=g[i][j]; } for(int x=1; x<=nx; x++){ for(int i=1; i<=ny; i++) slack[i]=inf; while(true){ memset(visx, false, sizeof(visx)); memset(visy, false, sizeof(visy)); if(dfs(x))break; int d=inf; for(int i=1; i<=ny; i++) if(!visy[i] && d>slack[i]) d=slack[i]; for(int i=1; i<=nx; i++) if(visx[i]) lx[i]-=d; for(int i=1; i<=ny; i++){ if(visy[i])ly[i]+=d; else slack[i]-=d; } } } int res=0; for(int i=1; i<=ny; i++) if(link[i]!=-1) res+=g[link[i]][i]; return res;}void floyd(){ for(int k=0; k<n+m; k++) for(int i=0; i<n+m; i++) for(int j=0; j<n+m; j++) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);}int main(){ int T; scanf("%d", &T); while(T--){ mst(dp, 0); scanf("%d%d", &n, &m); for(int i=0; i<n+m; i++) for(int j=0; j<n+m; j++){ scanf("%d", &dp[i][j]); if(dp[i][j] == -1)dp[i][j] = inf; } floyd(); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++){ g[i][j]=dp[i-1][n+j-1]; if(g[i][j] >= inf)g[i][j]=0; } if(n < m){ for(int i=n+1; i<=m; i++) for(int j=1; j<=m; j++) g[i][j]=0; } else if(n > m){ for(int i=1; i<=n; i++) for(int j=m+1; j<=n; j++) g[i][j]=0; } nx = ny = max(n, m); printf("%d\n", KM()); } return 0;}
0 0
- 【玲珑杯 1047】【二分匹配 KM算法或者费用流】Best couple【定义男女生的距离为最短距离,求匹配之后使得总距离最大】
- 最大权二分匹配(最大费用流) —— Best couple (玲珑学院 1047)
- HDU 1533Going Home(KM算法求二分图最小权匹配或者最小费用最大流)
- POJ-2195 Going Home (最小费用最大流初学 && 最大权二分匹配—KM算法)
- km算法(求二分图带权的最大匹配)
- 求最大权二分匹配的KM算法
- 求二分图最大权匹配的km算法
- 求最大权二分匹配的KM算法
- 求最大权二分匹配的KM算法
- 二分图带权匹配的KM算法以及费用流建模
- HDU 2426 Interesting Housing Problem 二分匹配(KM模板)或者最小费用最大流
- 【最大权二分匹配的KM算法】
- HDU 4460 求任意两点最短距离的最大距离
- 求二分图最大权一个匹配(未必最大匹配),/费用流
- KM算法求完全匹配情况下的最大匹配
- 二分图 KM算法(求二分图带权值的最大匹配)
- 二分图带权匹配 KM算法与费用流模型建立
- 二分图带权匹配 KM算法与费用流模型建立
- [心得]机器学习基础之线性代数笔记
- Java多线程join()
- Centos6.5编译安装nginx时出现的问题
- linux 基本命令之文件处理命令
- Spring batch基础一
- 【玲珑杯 1047】【二分匹配 KM算法或者费用流】Best couple【定义男女生的距离为最短距离,求匹配之后使得总距离最大】
- 基于Agera的EventBus实现库
- poj 3020 Antenna Placement
- 169. Majority Element
- CodeForces 152E
- jquery遍历表格获取表格当前行数据
- 浅谈CSS外边距合并
- UbuntuServer安装tomcat与初始化设置(命令行)
- android知识回顾-------使用到的设计模式及举例