次小生成树

定义:

设G = (V, E)是连通的无向图,T是图G的一个最小生成树.如果有另外一棵树T1,T1 ≠ T,满足不存在树T',T' ≠ T,w(T') < w(T1),则称T1是图G的次小生成树.

算法

最简单的是,设T是G的最小生成树,依次枚举T的边并去掉,再求最小生成树,所得到的这些值的最小值就是次小生成树,由于最小生成树有N-1条边,这种方法就相当于运行了N次最小生成树的算法,算法的时间复杂度比较高.大约是N * M的数量级.

当然对于次小生成树,我们也可以用prim,kurskal算法求解。这两种算法的思路都是相同的,首先求出最小生成树,我们枚举每条不在最小生成树上的边,并把这条边放到最小生成树上面,然后就一定会形成环,那么我们在这条环路中取出一条最长的路(除了新加入的那一条边)。最终我们得到的权值就是次小生成树的权值。

prim

我们需要一个数组maxd来记录 i 到 j 的最大距离。(如果加了i-j这条边会形成环,那么这个环上权值最大的就是j到pre[minj]或者minj到pre[minj]的边其中一个)

pre[j]表示 j 由pre[j] 更新而来

我们还需要数组:connect[i][j]表示最小生成树中这条边有没有被用到,剩下的就是我们要去模拟算法解释里所说的删边以及添边的操作了

代码如下:

package 图论;

import java.util.Arrays;
import java.util.Scanner;

public class 次小生成树_prim {

	static int maxn = 1000, inf = (int) 1e9;
	static int[][] map = new int[maxn][maxn];
	static int[][] maxd = new int[maxn][maxn];// 二维数组maxd[i][j]表示最小生成树中i点到j点的最远距离
	static int[] dis = new int[maxn];
	static int[] pre = new int[maxn];
	static boolean[] vis = new boolean[maxn];
	static boolean[][] connect = new boolean[maxn][maxn];// 是否加入最小生成树

	static void init(int n) {
		for (int i = 0; i <= n; i++) {
			for (int j = 0; j <= n; j++) {
				if (i == j)
					map[i][j] = 0;
				else {
					map[i][j] = inf;
				}
			}
		}
	}

	static int prim(int n) {

		for (int i = 1; i <= n; i++) {
			dis[i] = map[1][i];// 开始最短距离都是1-i的距离
			pre[i] = 1;// 首先父亲节点都是根节点
		}
		vis[1] = true;
		dis[1] = 0;
		int res = 0;

		for (int i = 1; i < n; i++) {
			int mind = inf, minj = -1;
			for (int j = 1; j <= n; j++) {
				if (!vis[j] && mind > dis[j]) {
					minj = j;
					mind = dis[j];
				}
			}
			if (minj == -1)
				return res;
			vis[minj] = true;
			connect[minj][pre[minj]] = true;// 这条边已经在最小生成树中,后面我们就不能添加这条边了
			connect[pre[minj]][minj] = true;
			res += mind;
			maxd[minj][pre[minj]] = maxd[pre[minj]][minj] = mind;

			for (int j = 1; j <= n; j++) {
				if (!vis[j] && map[minj][j] < dis[j]) {
					dis[j] = map[minj][j];
					pre[j] = minj;// j点由minj更新而来
				}
				if (vis[j] && j != minj) {// 只是更新我们已经遍历过来的节点
					// 如果加了i-j这条边会形成环,那么这个环上权值最大的就是j到pre[minj]或者minj到pre[minj]的边其中一个
					maxd[minj][j] = maxd[j][minj] = Math.max(maxd[j][pre[minj]], dis[minj]);
				}
			}
		}
		return res;// 最小生成树权值之和
	}

	static void prim2(int n) {
		int min_ans = prim(n);
		System.out.println("min_ans " + min_ans);
		int ans = inf;
		// 枚举最小生成树之外的边
		for (int i = 1; i <= n; i++) {
			for (int j = i + 1; j <= n; j++) {
				// i-j有边且不在最小生成树里
				if (map[i][j] != inf && !connect[i][j]) {
					ans = Math.min(ans, min_ans + map[i][j] - maxd[i][j]);
				}
			}
		}
		if (ans == inf)
			System.out.println("无次小生成树");
		else
			System.out.println("second_ans " + ans);
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		init(n);
		for (int i = 0; i < m; i++) {
			int u = sc.nextInt();
			int v = sc.nextInt();
			int w = sc.nextInt();
			map[u][v] = map[v][u] = w;
		}
		prim2(n);
	}

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值