You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
4.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 注意本文件中graph不是邻接矩阵的含义而是一个二部图
// 在长度为N的邻接矩阵matrix中所有的点有N个matrix[i][j]表示点i到点j的距离或者权重
// 而在二部图graph中所有的点有2*N个行所对应的点有N个列所对应的点有N个
// 而且认为,行所对应的点之间是没有路径的,列所对应的点之间也是没有路径的!
package class_2022_03_5_week;
import java.util.Arrays;
// km算法
// O(N^3),最大匹配问题,最优的解!
public class Code01_KMAlgorithm {
// 暴力解
public static int right(int[][] graph) {
int N = graph.length;
int[] to = new int[N];
for (int i = 0; i < N; i++) {
to[i] = 1;
}
return process(0, to, graph);
}
public static int process(int from, int[] to, int[][] graph) {
if (from == graph.length) {
return 0;
}
int ans = 0;
for (int i = 0; i < to.length; i++) {
if (to[i] == 1) {
to[i] = 0;
ans = Math.max(ans, graph[from][i] + process(from + 1, to, graph));
to[i] = 1;
}
}
return ans;
}
public static int km(int[][] graph) {
int N = graph.length;
int[] match = new int[N];
int[] lx = new int[N];
int[] ly = new int[N];
// dfs过程中碰过的点
boolean[] x = new boolean[N];
boolean[] y = new boolean[N];
// 降低的预期!
// 公主上,打一个,降低预期的值,只维持最小!
int[] slack = new int[N];
int invalid = Integer.MAX_VALUE;
for (int i = 0; i < N; i++) {
match[i] = -1;
lx[i] = -invalid;
for (int j = 0; j < N; j++) {
lx[i] = Math.max(lx[i], graph[i][j]);
}
ly[i] = 0;
}
for (int from = 0; from < N; from++) {
for (int i = 0; i < N; i++) {
slack[i] = invalid;
}
Arrays.fill(x, false);
Arrays.fill(y, false);
// dfs() : from王子能不能不降预期匹配成功
// 能dfs返回true
// 不能dfs返回false
while (!dfs(from, x, y, lx, ly, match, slack, graph)) {
// 刚才的dfs失败了
// 需要拿到公主的slack里面预期下降幅度的最小值
int d = invalid;
for (int i = 0; i < N; i++) {
if (!y[i] && slack[i] < d) {
d = slack[i];
}
}
// 按照最小预期来调整预期
for (int i = 0; i < N; i++) {
if (x[i]) {
lx[i] = lx[i] - d;
}
if (y[i]) {
ly[i] = ly[i] + d;
}
}
Arrays.fill(x, false);
Arrays.fill(y, false);
// 然后回到while里再次尝试
}
}
int ans = 0;
for (int i = 0; i < N; i++) {
ans += (lx[i] + ly[i]);
}
return ans;
}
// from, 当前的王子
// x王子碰没碰过
// y, 公主碰没碰过
// lx所有王子的预期
// ly, 所有公主的预期
// match所有公主之前的分配之前的爷们
// slack连过但没允许的公主最小下降的幅度
// map报价所有王子对公主的报价
// 返回from号王子不降预期能不能配成
public static boolean dfs(int from, boolean[] x, boolean[] y, int[] lx, int[] ly, int[] match, int[] slack,
int[][] map) {
int N = map.length;
x[from] = true;
for (int to = 0; to < N; to++) {
if (!y[to]) { // 只有没dfs过的公主才会去尝试
int d = lx[from] + ly[to] - map[from][to];
if (d != 0) {// 如果当前的路不符合预期更新公主的slack值
slack[to] = Math.min(slack[to], d);
} else { // 如果当前的路符合预期,尝试直接拿下,或者抢夺让之前的安排倒腾去
y[to] = true;
if (match[to] == -1 || dfs(match[to], x, y, lx, ly, match, slack, map)) {
match[to] = from;
return true;
}
}
}
}
return false;
}
// 为了测试
public static int[][] randomGraph(int N, int V) {
int[][] graph = new int[N][N];
for (int i = 0; i < N; i++) {
for (int j = i + 1; j < N; j++) {
int num = (int) (Math.random() * V);
graph[i][j] = num;
graph[j][i] = num;
}
}
return graph;
}
// 为了测试
public static void main(String[] args) {
int N = 10;
int V = 20;
int testTime = 100;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int[][] graph = randomGraph(N, V);
int ans1 = right(graph);
int ans2 = km(graph);
if (ans1 != ans2) {
System.out.println("Oops!");
for (int r = 0; r < graph.length; r++) {
for (int c = 0; c < graph.length; c++) {
System.out.print(graph[r][c] + " ");
}
System.out.println();
}
System.out.println(ans1);
System.out.println(ans2);
}
}
System.out.println("测试结束");
}
}