算法参考的论文信息:
Subhash C. Narula, Cesar A. Ho,
Degree-constrained minimum spanning tree,
Computers & Operations Research,
Volume 7, Issue 4,
1980,
Pages 239-249,
ISSN 0305-0548,
https://doi.org/10.1016/0305-0548(80)90022-2.
(https://www.sciencedirect.com/science/article/pii/0305054880900222)
Abstract: In this paper the problem of a degree-constrained minimum spanning tree (DCMST) is defined. The problem is formulated as a linear 0–1 integer programming problem. A primal and a dual heuristic (construction) procedure and a branch-and-bound algorithm are proposed to construct a DCMST. These procedures are illustrated with a simple example. Some computational experience with these algorithms is also reported.
论文中相关算法的描述:
public class DCMST {
//输出:返回n-1行,表示最小生成树的n-1条链路,2列的数组,值为节点号,表示链路两端的节点
//输入:n:节点数;cost:代价矩阵;b:节点度限制向量
public int[][] primal(int n, double[][] cost, int[] b) {
/*
Step 1. Initialize
*/
List<Integer> F1 = new ArrayList<>();
List<Double> F2 = new ArrayList<>();
List<Integer> F3 = new ArrayList<>();
List<Integer> d = new ArrayList<>();
for (int k = 1; k <= n; k++) {
d.add(0);
}
int m = 1;
//p为1-n中的任意数
int p = new Random().nextInt(n) + 1;
for (int q = 1; q <= n; q++) {
if (q == p) continue;
F1.add(q);
F3.add(p);
F2.add(cost[p - 1][q - 1]);
}
HashSet<Integer> visited = new HashSet<>();
int[][] E = new int[n - 1][2];
while (true) {
/*
Step 2.
*/
double c_star = Integer.MAX_VALUE;
int i_star = 0;
//找到F2中的最小值,将index赋值给i_star
for (int i = 0; i < F2.size(); i++) {
if (F2.get(i) < c_star) {
c_star = F2.get(i);
i_star = i;
}
}
// r 和 s 分别对应 F3 和 F1中 i_star位置的卫星
int r = F3.get(i_star);
int s = F1.get(i_star);
if (d.get(r - 1) < b[r - 1]) {
E[m - 1][0] = r;
E[m - 1][1] = s;
d.set(r - 1, d.get(r - 1) + 1);
d.set(s - 1, d.get(s - 1) + 1);
visited.add(r);
visited.add(s);
/*
Step 4.
*/
F1.remove(i_star);
F2.remove(i_star);
F3.remove(i_star);
for (int i = 0; i < F1.size(); i++) {
double Gi = cost[s - 1][F1.get(i) - 1];
if (Gi < F2.get(i)) {
F2.set(i, Gi);
F3.set(i, s);
}
}
m = m + 1;
if (m > n - 1) {
break;
}
} else if (d.get(r - 1) == b[r - 1]) {
/*
Step 3.
*/
for (int j = 0; j < F3.size(); j++) {
if (F3.get(j) == r) {
int t = F1.get(j);
double minCost = 99;
int u = -1;
for (int uu = 1; uu <= n; uu++) {
if (uu != t && visited.contains(uu) && d.get(uu - 1) >= 0 && d.get(uu - 1) < b[uu - 1] && cost[uu - 1][t - 1] < minCost) {
minCost = cost[uu - 1][t - 1];
u = uu;
}
}
if(u==-1) {
return new int[0][0];
}
F3.set(j, u);
F2.set(j, cost[t - 1][u - 1]);
}
}
}
}
/*
The improvement edge exchange algorithm
*/
unionFind u;
for (int i = 0; i < E.length; i++) {
int ii = E[i][0];
int jj = E[i][1];
u = new unionFind(n);
for (int j = 0; j < E.length; j++) {
if (i == j) continue;
int x = E[j][0];
int y = E[j][1];
u.union(x - 1, y - 1);
}
int s = 0;
List<Integer> Ti = new ArrayList<>();
List<Integer> Tj = new ArrayList<>();
Ti.add(s + 1);
for (int k = 1; k < n; k++) {
if (u.find(s) == u.find(k)) {
Ti.add(k + 1);
} else {
Tj.add(k + 1);
}
}
boolean flag = false;
for (int v : Ti) {
for (int w : Tj) {
if (cost[v - 1][w - 1] < cost[ii - 1][jj - 1] && d.get(v - 1) + 1 <= b[v - 1] && d.get(w - 1) + 1 <= b[w - 1]) {
E[i][0] = v;
E[i][1] = w;
d.set(v - 1, d.get(v - 1) + 1);
d.set(w - 1, d.get(w - 1) + 1);
d.set(ii - 1, d.get(ii - 1) - 1);
d.set(jj - 1, d.get(jj - 1) - 1);
flag = true;//evm exists!
break;
}
}
if (flag) {
break;
}
}
if (!flag) {
if (d.get(ii - 1) == b[ii - 1] || d.get(jj - 1) == b[jj - 1]) {
for (int v : Ti) {
for (int w : Tj) {
if (cost[v - 1][w - 1] == cost[ii - 1][jj - 1] && d.get(v - 1) + 1 <= b[v - 1] && d.get(w - 1) + 1 <= b[w - 1]) {
E[i][0] = v;
E[i][1] = w;
d.set(v - 1, d.get(v - 1) + 1);
d.set(w - 1, d.get(w - 1) + 1);
d.set(ii - 1, d.get(ii - 1) - 1);
d.set(jj - 1, d.get(jj - 1) - 1);
}
}
}
}
}
}
return E;
}
//使用并查集求点与点是否连通
public class unionFind {
int[] parent;
public unionFind(int num) {
parent = new int[num];
for (int i = 0; i < num; i++) {
parent[i] = i;
}
}
public int find(int x) {
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) return;
parent[rootX] = rootY;
}
}