Java实现二分图的最大权匹配
1 问题描述
何为二分图的最大权匹配问题?
最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大。
2 解决方案
解决这个问题可以用KM算法。理解KM算法需要首先理解“可行顶标”的概念。可行顶标是指关于二分图两边的每个点的一个值lx[i]或ly[j],保证对于每条边w[i][j]都有lx[i]+ly[j]-w[i][j]>=0。如果所有满足lx[i]+ly[j]==w[i][j]的边组成的导出子图中存在一个完美匹配,那么这个完美匹配肯定就是原图中的最大权匹配。理由很简单:这个匹配的权值之和恰等于所有顶标的和,由于上面的那个不等式,另外的任何匹配方案的权值和都不会大于所有顶标的和。
但问题是,对于当前的顶标的导出子图并不一定存在完美匹配。这时,可以用某种方法对顶标进行调整。调整的方法是:根据最后一次不成功的寻找交错路的DFS,取所有i被访问到而j没被访问到的边(i,j)的lx[i]+ly[j]-w[i][j]的最小值d。将交错树中的所有左端点的顶标减小d,右端点的顶标增加d。经过这样的调整以后:原本在导出子图里面的边,两边的顶标都变了,不等式的等号仍然成立,仍然在导出子图里面;原本不在导出子图里面的边,它的左端点的顶标减小了,右端点的顶标没有变,而且由于d的定义,不等式仍然成立,所以他就可能进入了导出子图里。
初始时随便指定一个可行顶标,比如说lx[i]=max{w[i][j]|j是右边的点},ly[i]=0。然后对每个顶点进行类似Hungary算法的find过程,如果某次find没有成功,则按照这次find访问到的点对可行顶标进行上述调整。这样就可以逐步找到完美匹配了。
值得注意的一点是,按照上述d的定义去求d的话需要O(N2)的时间,因为d需要被求O(N2)次,这就成了算法的瓶颈。可以这样优化:设slack[j]表示右边的点j的所有不在导出子图的边对应的lx[i]+ly[j]-w[i][j]的最小值,在find过程中,若某条边不在导出子图中就用它对相应的slack值进行更新。然后求d只要用O(N)的时间找到slack中的最小值就可以了。
下面代码所使用的测试数据如下图:
package com.liuzhen.practice;
import java.util.Scanner;
public class Main {
public static int MAX = 100;
public static int n;
public static int[][] value = new int[MAX][MAX]; //给定二分图的权重值
public static int[] lx = new int[MAX]; //记录二分图左半部分顶点的可行顶标
public static int[] ly = new int[MAX]; //记录二分图右半部分顶点的可行顶标
public static boolean[] sx = new boolean[MAX];//用于记录二分图左半部分顶点是否在最终结果中
public static boolean[] sy = new boolean[MAX];//用于记录二分图右半部分顶点是否在最终结果中
public static int[] pre = new int[MAX]; //用于记录最终结果中顶点y匹配的顶点x
public boolean dfs(int x) { //采用匈牙利算法找增广路径
sx[x] = true; //代表左半部分顶点x包含在最终结果中
for(int y = 0;y < n;y++) {
if(!sy[y] && lx[x] + ly[y] == value[x][y]) {
sy[y] = true; //代表右半部分顶点y包含在最终结果中
if(pre[y] == -1 || dfs(pre[y])) {
pre[y] = x;
return true;
}
}
}
return false;
}
public int getKM(int judge) {
if(judge == -1) { //代表寻找二分图的最小权匹配
for(int i = 0;i < n;i++)
for(int j = 0;j < n;j++)
value[i][j] = -1 * value[i][j]; //把权值变为相反数,相当于找最大权匹配
}
//初始化lx[i]和ly[i]
for(int i = 0;i < n;i++) {
ly[i] = 0;
lx[i] = Integer.MIN_VALUE;
for(int j = 0;j < n;j++) {
if(value[i][j] > lx[i])
lx[i] = value[i][j];
}
}
for(int i = 0;i < n;i++)
pre[i] = -1; //初始化右半部分顶点y的匹配顶点为-1
for(int x = 0;x < n;x++) { //从左半部分顶点开始,寻找二分图完美匹配的相等子图完美匹配
while(true) {
for(int i = 0;i < n;i++) {//每次寻找x的增广路径,初始化sx[i]和sy[i]均为被遍历
sx[i] = false;
sy[i] = false;
}
if(dfs(x)) //找到从x出发的增广路径,结束循环,寻找下一个x的增广路径
break;
//下面对于没有找到顶点x的增广路径进行lx[i]和ly[i]值的调整
int min = Integer.MAX_VALUE;
for(int i = 0;i < n;i++) {
if(sx[i]) { //当sx[i]已被遍历时
for(int j = 0;j < n;j++) {
if(!sy[j]) { //当sy[j]未被遍历时
if(lx[i] + ly[j] - value[i][j] < min)
min = lx[i] + ly[j] - value[i][j];
}
}
}
}
if(min == 0)
return -1;
for(int i = 0;i < n;i++) {
if(sx[i])
lx[i] = lx[i] - min;
if(sy[i])
ly[i] = ly[i] + min;
}
}
}
int sum = 0;
for(int y = 0;y < n;y++) {
System.out.println("y顶点"+y+"和x顶点"+pre[y]+"匹配");
if(pre[y] != -1)
sum = sum + value[pre[y]][y];
}
if(judge == -1)
sum = -1 * sum;
return sum;
}
public static void main(String[] args) {
Main test = new Main();
Scanner in = new Scanner(System.in);
n = in.nextInt();
int k = in.nextInt(); //给定二分图的有向边数目
for(int i = 0;i < k;i++) {
int x = in.nextInt();
int y = in.nextInt();
int v = in.nextInt();
value[x][y] = v;
}
System.out.println(test.getKM(1));
}
}
运行结果:
10
0 2
1 3
0 2
0 4
2 2
2 1
3 3
4 2
3 8
4 3
y顶点0和x顶点2匹配
y顶点1和x顶点0匹配
y顶点2和x顶点1匹配
y顶点3和x顶点4匹配
y顶点4和x顶点3匹配
Java实现二分图的最大权匹配的更多相关文章
- 算法笔记_139:二分图的最大权匹配(Java)
目录 1 问题描述 2 解决方案 1 问题描述 何为二分图的最大权匹配问题? 最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大. 2 解决方案 对于此问题的讲解 ...
- hdu 3722 Card Game 二分图的最大权匹配
题目可以转化为2个集合,x集合和y集合,其中的元素是1-n个字符串. 首先预处理点与点的边权,然后直接用二分图的最大权匹配模板. #include<stdio.h> #include< ...
- km算法(二分图最大权匹配)学习
啦啦啦! KM算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转 化为求完备匹配的问题的.设顶点Xi的顶标为A[i],顶点Yi的顶标为B[i],顶点Xi与Yj之间的边权为w[i,j].在 ...
- 二分图 最大权匹配 km算法
这个算法的本质还是不断的找增广路: KM算法的正确性基于以下定理:若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最 ...
- “亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 A noj 2073 FFF [ 二分图最大权匹配 || 最大费用最大流 ]
传送门 FFF 时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte总提交 : 145 测试通过 : 13 ...
- HDU2255 奔小康赚大钱 —— 二分图最大权匹配 KM算法
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others) ...
- HDU3488 Tour —— 二分图最大权匹配 KM算法
题目链接:https://vjudge.net/problem/HDU-3488 Tour Time Limit: 3000/1000 MS (Java/Others) Memory Limit ...
- POJ2195 Going Home[费用流|二分图最大权匹配]
Going Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 22088 Accepted: 11155 Desc ...
- hdu 2255 二分图最大权匹配 *
题意:说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子.这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房 ...
随机推荐
- C++内存管理学习笔记(2)
/****************************************************************/ /* 学习是合作和分享式的! /* Auth ...
- git仓促拉去提交输入密码读取钥匙串
项目的目录执行: git config --global credential.helper osxkeychain
- pod install update没有反应
出去是镜像的问题,我用的是外网,所以应该不是镜像的问题, 换用这个: pod install --verbose --no-repo-update pod update --verbose --no- ...
- 题解 P2421 【[NOI2002]荒岛野人】
我的第一道数论紫题 首先,我们先看两个野人,他们相遇的充要条件是 \(C_i+P_i\times k\equiv C_j+P_j\times k\;(mod\;M)\) 其中\(k\)是第几年,且\( ...
- onmouseenter,onmouseleave,onmouseover,onmouseout的区别
首先,这四个事件两两配对使用,onmouseenter.onmouseleave一对,onmouseover.onmouseout一对,不能混合使用. onmouseenter 和 onmousele ...
- 黑马程序员_毕向东_Java基础视频教程——变量(随笔)
变量 变量 就是将不确定的数据进行存储.也就是需要在内存中开辟一块空间. 内存就是一块运算区域.内存越大.内存中装的数位就越多.运算就越快 ============================== ...
- jenkins 流水线学习
最佳实践: https://www.cnblogs.com/itech/p/5678643.html 一些样例 https://jenkins.io/doc/pipeline/examples/ gi ...
- Java基础之值传递
一.传递类型 我们从c语言开始学习程序设计语言时就知道,参数的传递类型一般有两种:值传递和引用传递.那么什么是值传递什么是引用传递呢? 值传递:指在调用方法时将实际参数的值拷贝一份传递给方法,这样方法 ...
- MCP3421使用详解
0 摘要 因某项目需要,需要采集微弱的电压信号,且对电压精度要求较高,于是选中MCP3421这款18 bit 高精度IIC AD转换芯片.本文将结合MCP3421的手册,对该芯片的使用进行详细解释,并 ...
- flask之CORS跨域请求处理
from flask import Flask from flask_cors import CORS#pip install Flask-CORS#跨域请求模块 app = Flask(__name ...