#include <iostream>
#include <tuple>
#include <algorithm>
#include <limits>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
using std::vector;
struct Graph_link_node{
double val;
int next;
int to;
};
struct Edge{
double val;
int from, to;
public:
Edge(double val, int from, int to){
this->val = val;
this->from= from;
this->to = to;
}
};
class Graph{
private:
int verticle;
int edge;
vector<info::Graph_link_node> nodes;
int count;
vector<int> heads;
private:
// used before initialization
void set_count_zero(){ count = 1; }
void add_node(int from, int to, double val){
nodes[count].next = heads[from];
nodes[count].to = to;
nodes[count].val = val;
heads[from] = count;
count++;
}
public:
Graph(FILE* input){
fscanf(input, "%d%d", &verticle, &edge);
heads.resize(verticle+10, -1);
nodes.resize(2*edge+10);
set_count_zero();
for (int i=0; i<edge; i++){
int from, to;
double val;
fscanf(input, " %d%d%lf", &from, &to, &val);
add_node(from, to, val);
add_node(to, from, val);
}
}
void print(){
putchar('\n');
for (int from=0; from<verticle; from++){
for (int index=heads[from]; index!=-1; index=nodes[index].next){
int to = nodes[index].to;
double val = nodes[index].val;
printf("%2d\t%2d\t%6.2f\n", from, to, val);
}
}
}
// 管梅谷破圈算法
// https://blog.csdn.net/qazwsxrx/article/details/104904220#:~:text=%E7%A0%B4%E5%9C%88%E6%B3%95%20%28%E7%AE%A1%E6%A2%85%E8%B0%B7%29%20%E7%AE%A1%E6%A2%85%E8%B0%B7%EF%BC%88%EF%BC%91%EF%BC%99%EF%BC%93%EF%BC%94%EF%BC%8D%EF%BC%89%E3%80%82%20%E6%88%91%E5%9B%BD%E8%91%97%E5%90%8D%E6%95%B0%E5%AD%A6%E5%AE%B6%EF%BC%8C%E6%9B%BE%E4%BB%BB%E5%B1%B1%E4%B8%9C%E5%B8%88%E8%8C%83%E5%A4%A7%E5%AD%A6%E6%A0%A1%20%E9%95%BF%E3%80%82%20%E4%B8%AD%E5%9B%BD%E8%BF%90%E7%AD%B9%E5%AD%A6%E4%BC%9A%E7%AC%AC%E4%B8%80%E3%80%81%E4%BA%8C%E5%B1%8A%E5%B8%B8%E5%8A%A1%E7%90%86%E4%BA%8B%EF%BC%8C%E7%AC%AC%E5%85%AD%E5%B1%8A%E5%85%A8%E5%9B%BD%E6%94%BF%E5%8D%8F%E5%A7%94%E5%91%98%E3%80%82%20%E4%BB%8E%20%E4%BA%8B%E8%BF%90%E7%AD%B9%E5%AD%A6%E5%8F%8A%E5%85%B6%E5%BA%94%E7%94%A8%E7%9A%84%E7%A0%94%E7%A9%B6%EF%BC%8C%E5%AF%B9%E6%9C%80%E7%9F%AD%E6%8A%95%E9%80%92%E8%B7%AF%E7%BA%BF%E9%97%AE%E9%A2%98%E7%9A%84%E7%A0%94%E7%A9%B6%E5%8F%96%E5%BE%97%E6%88%90%E6%9E%9C,%E5%86%A0%E5%90%8D%E4%B8%BA%E4%B8%AD%E5%9B%BD%E9%82%AE%E8%B7%AF%E9%97%AE%E9%A2%98%EF%BC%8C%E8%AF%A5%E9%97%AE%E9%A2%98%E8%A2%AB%E5%88%97%E5%85%A5%E7%BB%8F%E5%85%B8%E5%9B%BE%E8%AE%BA%E6%95%99%E6%9D%90%20%E5%92%8C%E8%91%97%E4%BD%9C%E3%80%82%20%E6%80%9D%E8%B7%AF%EF%BC%9A%E4%BB%8E%E8%B5%8B%E6%9D%83%E5%9B%BEG%E7%9A%84%20%E4%BB%BB%E6%84%8F%E5%9C%88%E5%BC%80%E5%A7%8B%20%EF%BC%8C%20%E5%8E%BB%E6%8E%89%E8%AF%A5%E5%9C%88%E4%B8%AD%E6%9D%83%E5%80%BC%E6%9C%80%E5%A4%A7%E7%9A%84%E4%B8%80%E6%9D%A1%E8%BE%B9%EF%BC%8C%E7%A7%B0%E4%B8%BA%E7%A0%B4%E5%9C%88%E3%80%82%20%E4%B8%8D%E6%96%AD%E7%A0%B4%E5%9C%88%EF%BC%8C%E7%9B%B4%E5%88%B0G%E4%B8%AD%E6%B2%A1%20%E6%9C%89%E5%9C%88%E4%B8%BA%E6%AD%A2%EF%BC%8C%E6%9C%80%E5%90%8E%E5%89%A9%E4%B8%8B%E7%9A%84G%E7%9A%84%E5%AD%90%E5%9B%BE%E4%B8%BAG%E7%9A%84%E6%9C%80%E5%B0%8F%E7%94%9F%E6%88%90%E6%A0%91%E3%80%82
//
void guan_meigu(){
// 算法大致思路:先找到图内任意一个圈
// 然后破除掉该圈中权值最大的一个边
// 直到整个图都没有圈为止
printf("== heads ==\n");
for (int i=0; i<verticle; i++){
printf("%d\t%d\n", i, heads[i]);
}
printf("== nodes ==\n");
int size = nodes.size();
for (int i=0; i<size; i++){
auto& node = nodes[i];
printf("%2d\t%2d\t%2d\t%f\n", i, node.to, node.next, node.val);
}
// 用于保存顶点访问轨迹
// a[i][0]用于存储前一个顶点
// a[i][1]用于存储连接轨迹中顶点i和它后一个顶点的边所在结点
// 如果a[i][1]==-1,说明该顶点的所有与之联通的结点都已访问
// 此时通过a[i][0]来访问其前一个顶点
// 如果a[i][0]==-1,则访问结束
// 若 from->to 边在 nodes 中的下标为 index
// 则 a[from] = (/, index);
// a[to] = (from, /);
vector<vector<int>> a(verticle, vector<int>({-1, -1}));
// 用来保存某条边是否被裁剪过
vector<vector<int>>
is_trimmed(verticle, vector<int>(verticle, 0));
int pre = 0,
index = heads[0],
cur = nodes[index].to,
iindex= 0;
a[0] = {-1, index};
a[cur]={pre, -1};
printf("== run algorithm ==\n");
do{
// 每次循环开始,获得一个pre顶点和index边
// 检查该边是否可访问
if (index == -1){// 如果不可访问当前边
POP: // 可看做从访问队列中弹出一个顶点,故命名为POP
// 将当前顶点切换到pre顶点
cur = pre;
// 将pre顶点切换到cur顶点的前顶点
pre = a[cur][0];
if (pre == -1) break;
// 将当前顶点的前顶点置空
a[cur][0] = -1;
// 将index置为和前顶点相连的下一条边
index = a[pre][1];
index = nodes[index].next;
a[pre][1] = index;
continue;
}
// 从当前边获取当前顶点
cur = nodes[index].to;
// a[cur][0] = pre;
if (is_trimmed[pre][cur]){ // 如果当前边被裁剪
a[pre][1] = index = nodes[index].next;
continue;
}
if (a[cur][1] == -1){ // 如果cur没有被访问过
// 则深度优先访问
a[cur][0] = pre;
a[cur][1] = index = heads[cur];
pre = cur;
continue;
} else { // 否则,分为两种情况
iindex = a[cur][1];
if (nodes[iindex].to == pre) { // 说明遇到二元环
index = nodes[index].next;
a[pre][1] = index;
continue;
}
// 保存 a[cur] 状态
// 因为下面访问环的时候要清空整个环的访问状态
vector<int> cur_state = a[cur];
int ppre = pre,
ccur = cur,
iindex = index;
a[ppre][0] = a[ppre][1] = -1;
double max_distance = nodes[index].val;
int max_from = ppre, max_to = ccur;
while (ccur != pre){
// 沿着环遍历,同时清除环上的数据
iindex = a[ccur][1];
a[ccur][0] = a[ccur][1] = -1;
ppre = ccur;
ccur = nodes[iindex].to;
double distance = nodes[iindex].val;
if (distance >= max_distance){
max_distance = distance;
max_from = ppre;
max_to = ccur;
}
}
//剪枝
is_trimmed[max_from][max_to] =
is_trimmed[max_to][max_from] = 1;
// 恢复 a[cur] 状态
a[cur] = cur_state;
pre = cur;
index = a[cur][1];
continue;
}
} while (pre != -1);
printf("\n");
printf("== output result ==\n");
// 重新遍历图,并输出边
for (int from = 0; from < verticle; from++){
for (index = heads[from]; index != -1; index = nodes[index].next){
int to = nodes[index].to;
if (from >= to) continue;
if (is_trimmed[from][to]) continue;
printf("(%2d,%2d):%f\n", from, to, nodes[index].val);
}
}
printf("== finish ==\n");
return;
}
};
int main(){
const char* file_name = "data_graph.txt";
FILE* file = fopen(file_name, "r");
if (file == nullptr){
printf("Unable to open %s\n", file_name);
return 0;
}
printf("= guan_meigu =\n");
graph.guan_meigu();
putchar('\n');
fclose(file);
return 0;
}
输入数据与其对应的图:
10 16
0 1 4
0 2 8
1 2 11
1 3 8
2 4 7
2 6 1
3 4 2
3 5 2
3 7 7
3 8 4
4 6 6
5 6 2
6 8 2
7 8 14
7 9 9
8 9 10