无向图最小生成树
Prim算法
这个实现做的非常不好,需要改进。对付稠密图,Prim更胜Kruskal一筹。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
struct Node{ vector<int> to; vector<int> cost; int mi; int parent; bool vis; void init(){ to.clear(); cost.clear(); mi = 0x3f3f3f3f; vis = false; parent = -1; } void addEdge(int _to, int _cost){ to.push_back(_to); cost.push_back(_cost); } } vertex[100005]; int n = 0, m = 0; struct snode{ int key; int v; friend bool operator < (snode a,snode b){ return a.key > b.key; } }sn[100005]; struct Node_cmp{ bool operator () (int &a, int &b){ return vertex[a].mi > vertex[b].mi; } }; void MST_Prim(){ for (int i = 1; i <= n ; i++) { sn[i].key = 0x3f3f3f3f; sn[i].v = i; } priority_queue<snode> que; sn[1].key = 0; que.push(sn[1]); while (!que.empty()) { int cur = que.top().v; que.pop(); if(vertex[cur].vis){ continue; } vertex[cur].vis = true; for (int i = 0; i < vertex[cur].to.size(); i++) { int nxt = vertex[cur].to[i]; if (!(vertex[nxt].vis) && vertex[cur].cost[i] < sn[nxt].key) { sn[nxt].key = vertex[cur].cost[i]; vertex[nxt].parent = cur; que.push(sn[nxt]); } } } } |
Kruskal算法
[这个实现等待填坑]
Borůvka算法
[这个实现等待填坑]
有向图最小生成树(最小树形图)
复杂度O(V * E),以下代码仅存储边,而且会对存储结构发生破坏,请注意。实现来源于国立台湾师范大学。我对这份代码进行了更加详细的注释。
适用于固定了生成树的根的情形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
#pragma mark - Chu-Liu-Edmonds Algorithm for Minimum Arborescence (MST on Directed Graph) (MA_CLE) //注意:会破坏给出的图,可能需要备份 //最小树形图 int V, E; struct Edge {int a, b, c;} edge[40000]; int d[1000], p[1000], v[1000], n[1000], m[1000]; // 每個點最小入邊的權重,每個點最小入邊的來源, // 拜訪過,水母環,已收縮。 int MA_CLE(int r){ memset(m, 0, sizeof(m)); // 目前生成樹的權重,累計收縮水母環而失去的權重。 int w1 = 0, w2 = 0; while (true) { // 一旦形成生成樹就停止。最多執行V-1次{ /* O(E) graph traversal. find minimum in-edge for each vertice. --->o */ memset(d, 1, sizeof(d)); memset(p, -1, sizeof(p)); for (int i=0; i<E; ++i) { //iterate every edges int& a = edge[i].a; int& b = edge[i].b; int& c = edge[i].c; if (a != b && b != r && c < d[b]) d[b] = c, p[b] = a;//对于每一个节点,找到指向它的边中最短的那一条(我们要的是“最小”树形图) } /* O(V) jellyfish detection ___ / \ \___/ _/|||\_ //1\\ */ memset(v, -1, sizeof(v)); memset(n, -1, sizeof(n)); w1 = 0;//重置上一次已集计的边权总和 bool jf = false;//用来标记是不是发现了“水母” for (int i=0; i<V; ++i) { //iterate every point if (m[i]) { continue; //这种情况下表明这个点已经被缩入某个代表着“水母”的节点了 } if (p[i] == -1 && i != r) { return 1e9; //这种情况表明当前点并没有任何可用的入边,而且这个点还不是根部,那就表明最小树形图已经没解了,别玩了 } if (p[i] >= 0) { w1 += d[i]; //这里用来集计所有的最短入边,如果这是最后一次的while循环,这时加入w1的边都会被选中作为答案 } // 找水母環 int s; for (s = i; s != -1 && v[s] == -1; s = p[s]){ v[s] = i;//沿着当前点不断逆着箭头走,如果有环的话,这样最后一定会走回到环的接点停下,如果没环的话,一定会走到路径的端末停下,我们这么走一遭,把走过的都标记上,就得到一只“水母”(“水母”不一定是水母形状) } // 標記水母環上的點,以及將會被收縮掉的點。 if (s != -1 && v[s] == i) { //这时候的s是这次找出的环的接点,但不是路径的端末。我们要收缩的是由从当前回的i号点发动的找环中找出的的环 jf = true;//标记上已经找到“水母” int j = s;//copy for loop do { n[j] = s; //标记属于哪个水母环 m[j] = 1;//标记当前点已经被收缩了 w2 += d[j];//集计当前水母环的边权(边将会加入w2,并一直保持选中到最后) j = p[j];//沿着路径推进 } while (j != s);//直到回到了环的接点 m[s] = 0;//收缩环操作的起点,我们把他当成环的代表点,所以要取消收缩 } } if (!jf) break;//没找到水母环的话,后面的事情可以免了 /* O(E) edge reweighting and cycle contraction ___ / \ <- \___/ */ for (int i=0; i<E; ++i) { int& a = edge[i].a; int& b = edge[i].b; int& c = edge[i].c; if (n[b] >= 0){ c -= d[b]; //删掉已经被收缩的边的边权,注意不是清零,要领会“缩入”这一动作 } if (n[a] >= 0) { a = n[a]; //重定向边的端点至已经缩为一点的水母环 } if (n[b] >= 0) { b = n[b]; //同上 } if (a == b) { edge[i--] = edge[--E]; //如果当前边成了自环,这条边就可以不要了 } } } return w1 + w2; //结算:所有加入w2和w1的边,都是选中的边,要想输出步骤,在这里要加以纪录。 } #pragma mark - |
适用于生成树的根无法确定的情形
引用自GentieH,谢谢。
建立虚拟节点x,令x连所有的点,权值为所有边权的总和+1,求得最后结果减去该值即可,由于虚拟节点的存在,不会显式地出现孤立点,这时通过判断如果存在多与1个节点的pre为root,则说明无解。如果需要求出根节点,由于之前将x与图中的点连边时,我们按顺序连的话,考虑到可以用边去标识点,对于每一个终点v,如果它的pre为root,则标记这条边who,在求结果时,让who-E即可。
要求输出方案的情形
CF240E
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
#pragma mark - Chu-Liu-Edmonds Algorithm for Minimum Arborescence (MST on Directed Graph) (MA_CLE) //注意:会破坏给出的图,可能需要备份 //最小树形图 const int MAXN = 100005,MAXE = 200005; int V, E; struct Edge {int a, b;int c;int id;} edge[MAXE],orig_edge[MAXE]; int d[MAXN],dd[MAXN]; int p[MAXN], v[MAXN], n[MAXN], m[MAXN]; // 每個點最小入邊的權重,每個點最小入邊的來源, // 拜訪過,水母環,已收縮。 int used[MAXE*10]; int alter[MAXE*10][2]; int id[MAXN],pos[MAXN]; int MA_CLE(int root) { int nall=E,ans=0; while(true) { fill(d, d+MAXN, 1 << 30); memset(pos,-1,sizeof(pos)); memset(id,-1,sizeof(pos)); for (int i = 0; i < E; i++) { if (edge[i].a != edge[i].b && d[edge[i].b] > edge[i].c) { d[edge[i].b] = edge[i].c; p[edge[i].b] = edge[i].a; dd[edge[i].b] = edge[i].id; } } d[root]=0; int nCircle=-1; for (int i = 0; i < V; i++) { if (d[i] == 1 << 30) { return -1; } if (i != root) { used[dd[i]]++; } int v; ans += d[i]; for (v = i; v != root && pos[v] == -1; v = p[v]) { pos[v] = i; } if (v != root && id[v] == -1 && pos[v] == i) { id[v] = ++nCircle; for (int j = p[v]; j != v; j = p[j]) { id[j] = nCircle; } } } if (nCircle == -1) { break; } for (int i = 0; i < V; i++) { if (id[i] == -1) { id[i] = ++nCircle; } } for (int i = 0; i < E;i++) { int &a = edge[i].a; int &b = edge[i].b; int &c = edge[i].c; int pre = dd[b]; c -= d[b]; a = id[a],b = id[b]; if (a != b) { alter[nall][0] = edge[i].id; alter[nall][1] = pre; edge[i].id = nall++; } } V=nCircle+1,root=id[root]; } for (int i = nall-1; i >= E; i--) { if (used[i]) { used[alter[i][0]]++; used[alter[i][1]]--; } } return ans; } #pragma mark - int main(){ freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); int n,m; scanf(" %d %d",&n,&m); V = n,E = 0; int sumc = 0; for (int i = 1; i <= m; i++) { int a,b,c; scanf(" %d %d %d",&a,&b,&c); edge[E].a = a-1; edge[E].b = b-1; edge[E].c = c; edge[E].id = E; sumc += c; E++; } memcpy(orig_edge, edge, sizeof(edge)); int res = MA_CLE(0); if (res == -1) { puts("-1"); }else{ printf("%d\n",res); if (res) { for (int i = 0; i < m; i++) { if (used[i] && orig_edge[i].c == 1) { printf("%d ",i+1); } } printf("\n"); } } } |
次小生成树
[Pending Session]
增量最小生成树
[Pending Session]
最小瓶颈生成树
[Pending Session]