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实现二分图的最大权匹配的更多相关文章

  1. 算法笔记_139:二分图的最大权匹配(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 何为二分图的最大权匹配问题? 最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大. 2 解决方案 对于此问题的讲解 ...

  2. hdu 3722 Card Game 二分图的最大权匹配

    题目可以转化为2个集合,x集合和y集合,其中的元素是1-n个字符串. 首先预处理点与点的边权,然后直接用二分图的最大权匹配模板. #include<stdio.h> #include< ...

  3. km算法(二分图最大权匹配)学习

    啦啦啦! KM算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转 化为求完备匹配的问题的.设顶点Xi的顶标为A[i],顶点Yi的顶标为B[i],顶点Xi与Yj之间的边权为w[i,j].在 ...

  4. 二分图 最大权匹配 km算法

    这个算法的本质还是不断的找增广路: KM算法的正确性基于以下定理:若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最 ...

  5. “亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 A noj 2073 FFF [ 二分图最大权匹配 || 最大费用最大流 ]

    传送门 FFF 时间限制(普通/Java) : 1000 MS/ 3000 MS          运行内存限制 : 65536 KByte总提交 : 145            测试通过 : 13 ...

  6. HDU2255 奔小康赚大钱 —— 二分图最大权匹配 KM算法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2255 奔小康赚大钱 Time Limit: 1000/1000 MS (Java/Others)    ...

  7. HDU3488 Tour —— 二分图最大权匹配 KM算法

    题目链接:https://vjudge.net/problem/HDU-3488 Tour Time Limit: 3000/1000 MS (Java/Others)    Memory Limit ...

  8. POJ2195 Going Home[费用流|二分图最大权匹配]

    Going Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22088   Accepted: 11155 Desc ...

  9. hdu 2255 二分图最大权匹配 *

    题意:说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子.这可是一件大事,关系到人民的住房问题啊.村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房 ...

随机推荐

  1. TC SRM498 Div1 1000PT(容斥原理+DP)

    [\(Description\)] 网格中每步可以走\((0,\cdots M_x,0\cdots M_y)\)中任意非零向量,有\(K\)种向量不能走,分别是\((r_1,r_1),(r_2,r_2 ...

  2. 数据库当中删除数据后主键id不连续的问题

    新建查询: ALTER TABLE `表名` DROP `主键名`;ALTER TABLE `表名` ADD `主键名` int NOT NULL FIRST;ALTER TABLE `表名` MOD ...

  3. python之unittest验证函数功能

    一.待验证脚本 首先,有如下三个脚本,分别对应三个函数 分别导入模块行,如下: from fuction1 import fu1 from fuction2 import fu2 from fucti ...

  4. jar包名修改工具

    jar包名修改工具 软件传送门:链接: https://pan.baidu.com/s/12StRdEkYGmMn1DuNSquXSw   提取码: i9w1 闲暇之余,利用jarjar.jar写了一 ...

  5. .Net Core3.0 WebApi 项目框架搭建 一:实现简单的Resful Api

    .Net Core3.0 WebApi 项目框架搭建:目录 开发环境 Visual Studio 2019.net core 3.1 创建项目 新建.net core web项目,如果没有安装.net ...

  6. react 动态渲染echarts折线图,鼠标放大缩小

    //折线图组件import React,{Component} from 'react'; import ReactEcharts from 'echarts-for-react'; class Ec ...

  7. Wfuzz使用学习

    工具用了不总结,使用命令很容易生疏,今天就把笔记梳理总结一下. 0x01 简介 WFuzz是用于Python的Web应用程序安全性模糊工具和库.它基于一个简单的概念:它将给定有效负载的值替换对FUZZ ...

  8. sqlite聚合函数

    常见聚合函数 avg(X) 用于返回组中所有非空列的平均值.字符串(string)或二进制数据(BLOB)等非数字类型当作0来计算.结果是浮点型的数据,即便所有数据中只有一个整数(integer)的数 ...

  9. docer run 、docker attach 与 docker exec的区别

    进入容器的方式有以下三种: 使用ssh登陆进容器 使用nsenter.nsinit等第三方工具 使用Docker本身提供的工具 最佳方案为使用Docker本身提供的工具 docker run:创建和启 ...

  10. 在 n 道题目中挑选一些使得所有人对题目的掌握情况不超过一半。

    Snark and Philip are preparing the problemset for the upcoming pre-qualification round for semi-quar ...