有向图的最小树形图

合集下载

最小树与最小树形图(数学建模资料)

最小树与最小树形图(数学建模资料)

考虑弧(i,j)时,只需判断first(i)与 first(j)是否相 等,相等则端点属于同一单向链表;否则合并链表. 因此所有这些判断所需要的计算时间为O(m).
合并时, 我们总是把节点数较多的链表L’ 放在前面, 而把节点 数较少的链表L追加在后面.
16
Kruskal 算法的计算复杂性
合并后, 对于size和last的修改非常容易,可以在常数时间内完成; 但对 first的 修改必须对链表L中的每个元素(节点)进行, 复杂度为 O(h),h=size(L) . 这种合并最多进行(n-1)次, 对 first 进行修改的总的复杂度为 O(n log n) 记链表L追加在链表L’(size( L) h, size( L ) h , h h ) 后面而合并 成一个链表时的计算时间(操作次数)不超过 p1h(这里p1为常数)
R1
C13 C12
C24
R3
R2
R4
一般地,给定差异信息cij,如何确定存贮哪些行之间的差异元素, 使得存 贮空间尽可能少呢?这一问题可以用最小树问题来描述: 我们把矩阵每行 作为一个节点构成一个完全图, 第i个节点对应于矩阵第i行,并令弧(i,j) 上的权为cij. 对于存贮问题, 实际上只需要存贮一行的元素, 以及由该完 全图的一棵支撑树所对应的差异元素. 最小树就对应于最优的存贮方案.
O(m log n mn) O(mn)
O(n3 )
15
Kruskal 算法的计算复杂性改进
算法实现改进:利用三个数组
size - 用来记录每个链表中所含节点的个数(链表规模); last - 用来记录每个链表中最后的节点编号 first - 用来记录每个节点所在链表的第一个节点. 如果链表L={1,2,4,5} ,则size(L)=|L|=4, last(L)=5, first(1)= first(2)= first(4) = first(5)=1.

最小树形图——朱刘算法学习笔记

最小树形图——朱刘算法学习笔记

最⼩树形图——朱刘算法学习笔记问题描述给定⼀张⽆向图和⼀个根r,求出⼀个n-1条边的⼀张⼦图,使得从r出发可以到达任意⼀个点,同时使得所有选择的边权之和最⼩。

根据最⼩树形图的定义,这张图的除了根的每⼀个点都必须有且仅有⼀个⼊度。

那么我们可以贪⼼⼀点,对于除了根的所有点都找出⼀条连向它的边且边权最⼩,称作这个点的代表边,并把这些边权加⼊答案中。

然后我们找出了⼀张图,这张图中每个点都只有⼀个⼊度。

如果不算根的话,它应当是⼀个外向基环树森林。

然后我们找到所有的环,把它们缩成⼀个点。

再去扫描不在环内的边,假设有u->v,那么这条边的边权要减掉v的代表边权。

因为v是⼀个环,如果继续连u->v的边的话,相当于是把原来v的⽗亲断开,再连上u。

于是我们就完成了缩点,⼀直做下去,知道图中没有环。

⽆解就是图中除了根有点没有⼊度。

代码细节1、初始化,我们令id[]表⽰这个点在那个环⾥,top[]表⽰环顶(找环时⽤),mi[]表⽰代表边的边权,cnt表⽰环数。

2、找环的时候,因为是⼀颗外向基环树,所以我们对于每个点记录father,这样father就变成了内向基环树,这样可以⽅便找环。

3、没有和其他点组成环的让它⾃⼰成为⼀个环。

4、每做完⼀轮之后要更新⼀下点数和根。

代码#include<iostream>#include<cstdio>#define N 109#define M 10009#define inf 2e9using namespace std;int tot,top[N],id[N],cnt,mi[N],n,m,fa[N],r;long long ans;inline int rd(){int x=0;char c=getchar();bool f=0;while(!isdigit(c)){if(c=='-')f=1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}return f?-x:x;}struct edge{int from,to,l;}e[M];inline void add(int u,int v,int l){e[++tot].to=v;e[tot].l=l;e[tot].from=u;}inline bool solve(){while(1){for(int i=1;i<=n;++i)id[i]=top[i]=0,mi[i]=inf;for(int i=1;i<=m;++i)if(e[i].to!=e[i].from&&e[i].l<mi[e[i].to])fa[e[i].to]=e[i].from,mi[e[i].to]=e[i].l;int u=0;mi[r]=0;for(int i=1;i<=n;++i){if(mi[i]==inf)return0;ans+=mi[i];for(u=i;u!=r&&top[u]!=i&&!id[u];u=fa[u])top[u]=i;if(u!=r&&!id[u]){id[u]=++cnt;for(int v=fa[u];v!=u;v=fa[v])id[v]=cnt;}}if(!cnt)return1;for(int i=1;i<=n;++i)if(!id[i])id[i]=++cnt;for(int i=1;i<=m;++i){int num=mi[e[i].to];e[i].from=id[e[i].from];e[i].to=id[e[i].to];if(e[i].from!=e[i].to)e[i].l-=num;}n=cnt;r=id[r];cnt=0;}}int main(){n=rd();m=rd();r=rd();int u,v,w;for(int i=1;i<=m;++i){u=rd();v=rd();w=rd();add(u,v,w); }if(solve())cout<<ans;else puts("-1");return0;}。

ACM算法模板(吉林大学)

ACM算法模板(吉林大学)

目录目录 (1)Graph 图论 (3)|DAG的深度优先搜索标记 (3)|无向图找桥 (3)|无向图连通度(割) (3)|最大团问题DP+DFS (3)|欧拉路径O(E) (3)|D IJKSTRA数组实现O(N^2) (3)|D IJKSTRA O(E* LOG E) (4)|B ELLMAN F ORD单源最短路O(VE) (4)|SPFA(S HORTEST P ATH F ASTER A LGORITHM) (4)|第K短路(D IJKSTRA) (5)|第K短路(A*) (5)|P RIM求MST (6)|次小生成树O(V^2) (6)|最小生成森林问题(K颗树)O(MLOGM) (6)|有向图最小树形图 (6)|M INIMAL S TEINER T REE (6)|T ARJAN强连通分量 (7)|弦图判断 (7)|弦图的PERFECT ELIMINATION点排列 (7)|稳定婚姻问题O(N^2) (7)|拓扑排序 (8)|无向图连通分支(DFS/BFS邻接阵) (8)|有向图强连通分支(DFS/BFS邻接阵)O(N^2) (8)|有向图最小点基(邻接阵)O(N^2) (9)|F LOYD求最小环 (9)|2-SAT问题 (9)Network 网络流 (11)|二分图匹配(匈牙利算法DFS实现) (11)|二分图匹配(匈牙利算法BFS实现) (11)|二分图匹配(H OPCROFT-C ARP的算法) (11)|二分图最佳匹配(KUHN MUNKRAS算法O(M*M*N))..11 |无向图最小割O(N^3) (12)|有上下界的最小(最大)流 (12)|D INIC最大流O(V^2*E) (12)|HLPP最大流O(V^3) (13)|最小费用流O(V*E* F).......................................13|最小费用流O(V^2* F). (14)|最佳边割集 (15)|最佳点割集 (15)|最小边割集 (15)|最小点割集(点连通度) (16)|最小路径覆盖O(N^3) (16)|最小点集覆盖 (16)Structure 数据结构 (17)|求某天是星期几 (17)|左偏树合并复杂度O(LOG N) (17)|树状数组 (17)|二维树状数组 (17)|T RIE树(K叉) (17)|T RIE树(左儿子又兄弟) (18)|后缀数组O(N* LOG N) (18)|后缀数组O(N) (18)|RMQ离线算法O(N*LOG N)+O(1) (19)|RMQ(R ANGE M INIMUM/M AXIMUM Q UERY)-ST算法(O(NLOGN +Q)) (19)|RMQ离线算法O(N*LOG N)+O(1)求解LCA (19)|LCA离线算法O(E)+O(1) (20)|带权值的并查集 (20)|快速排序 (20)|2台机器工作调度 (20)|比较高效的大数 (20)|普通的大数运算 (21)|最长公共递增子序列O(N^2) (22)|0-1分数规划 (22)|最长有序子序列(递增/递减/非递增/非递减) (22)|最长公共子序列 (23)|最少找硬币问题(贪心策略-深搜实现) (23)|棋盘分割 (23)|汉诺塔 (23)|STL中的PRIORITY_QUEUE (24)|堆栈 (24)|区间最大频率 (24)|取第K个元素 (25)|归并排序求逆序数 (25)|逆序数推排列数 (25)|二分查找 (25)|二分查找(大于等于V的第一个值) (25)|所有数位相加 (25)Number 数论 (26)|递推求欧拉函数PHI(I) (26)|单独求欧拉函数PHI(X) (26)|GCD最大公约数 (26)|快速GCD (26)|扩展GCD (26)|模线性方程 A * X = B (% N) (26)|模线性方程组 (26)|筛素数[1..N] (26)|高效求小范围素数[1..N] (26)|随机素数测试(伪素数原理) (26)|组合数学相关 (26)|P OLYA计数 (27)|组合数C(N, R) (27)|最大1矩阵 (27)|约瑟夫环问题(数学方法) (27)|约瑟夫环问题(数组模拟) (27)|取石子游戏1 (27)|集合划分问题 (27)|大数平方根(字符串数组表示) (28)|大数取模的二进制方法 (28)|线性方程组A[][]X[]=B[] (28)|追赶法解周期性方程 (28)|阶乘最后非零位,复杂度O(NLOGN) (29)递归方法求解排列组合问题 (30)|类循环排列 (30)|全排列 (30)|不重复排列 (30)|全组合 (31)|不重复组合 (31)|应用 (31)模式串匹配问题总结 (32)|字符串H ASH (32)|KMP匹配算法O(M+N) (32)|K ARP-R ABIN字符串匹配 (32)|基于K ARP-R ABIN的字符块匹配 (32)|函数名: STRSTR (32)|BM算法的改进的算法S UNDAY A LGORITHM (32)|最短公共祖先(两个长字符串) (33)|最短公共祖先(多个短字符串)...............................33Geometry 计算几何.. (34)|G RAHAM求凸包O(N* LOG N) (34)|判断线段相交 (34)|求多边形重心 (34)|三角形几个重要的点 (34)|平面最近点对O(N* LOG N) (34)|L IUCTIC的计算几何库 (35)|求平面上两点之间的距离 (35)|(P1-P0)*(P2-P0)的叉积 (35)|确定两条线段是否相交 (35)|判断点P是否在线段L上 (35)|判断两个点是否相等 (35)|线段相交判断函数 (35)|判断点Q是否在多边形内 (35)|计算多边形的面积 (35)|解二次方程A X^2+B X+C=0 (36)|计算直线的一般式A X+B Y+C=0 (36)|点到直线距离 (36)|直线与圆的交点,已知直线与圆相交 (36)|点是否在射线的正向 (36)|射线与圆的第一个交点 (36)|求点P1关于直线LN的对称点P2 (36)|两直线夹角(弧度) (36)ACM/ICPC竞赛之STL (37)ACM/ICPC竞赛之STL简介 (37)ACM/ICPC竞赛之STL--PAIR (37)ACM/ICPC竞赛之STL--VECTOR (37)ACM/ICPC竞赛之STL--ITERATOR简介 (38)ACM/ICPC竞赛之STL--STRING (38)ACM/ICPC竞赛之STL--STACK/QUEUE (38)ACM/ICPC竞赛之STL--MAP (40)ACM/ICPC竞赛之STL--ALGORITHM (40)STL IN ACM (41)头文件 (42)线段树 (43)求矩形并的面积(线段树+离散化+扫描线) (43)求矩形并的周长(线段树+离散化+扫描线) (44)Graph 图论/*==================================================*\| DAG的深度优先搜索标记| INIT: edge[][]邻接矩阵; pre[], post[], tag全置0;| CALL: dfstag(i, n); pre/post:开始/结束时间\*==================================================*/int edge[V][V], pre[V], post[V], tag;void dfstag(int cur, int n){ // vertex: 0 ~ n-1pre[cur] = ++tag;for (int i=0; i<n; ++i) if (edge[cur][i]) {if (0 == pre[i]) {printf("Tree Edge!\n");dfstag(i,n);} else {if (0 == post[i]) printf("Back Edge!\n");else if (pre[i] > pre[cur])printf("Down Edge!\n");else printf("Cross Edge!\n");}}post[cur] = ++tag;}/*==================================================*\| 无向图找桥| INIT: edge[][]邻接矩阵;vis[],pre[],anc[],bridge 置0;| CALL: dfs(0, -1, 1, n);\*==================================================*/int bridge, edge[V][V], anc[V], pre[V], vis[V];void dfs(int cur, int father, int dep, int n){ // vertex: 0 ~ n-1if (bridge) return;vis[cur] = 1; pre[cur] = anc[cur] = dep;for (int i=0; i<n; ++i) if (edge[cur][i]) {if (i != father && 1 == vis[i]) {if (pre[i] < anc[cur])anc[cur] = pre[i];//back edge}if (0 == vis[i]) { //tree edgedfs(i,cur,dep+1,n);if (bridge) return;if (anc[i] < anc[cur]) anc[cur] = anc[i];if (anc[i] > pre[cur]) { bridge = 1; return; } }}vis[cur] = 2;}/*==================================================*\| 无向图连通度(割)| INIT: edge[][]邻接矩阵;vis[],pre[],anc[],deg[]置为0;| CALL: dfs(0, -1, 1, n);| k=deg[0], deg[i]+1(i=1…n-1)为删除该节点后得到的连通图个数| 注意:0作为根比较特殊!\*==================================================*/int edge[V][V], anc[V], pre[V], vis[V], deg[V];void dfs(int cur, int father, int dep, int n){// vertex: 0 ~ n-1int cnt = 0;vis[cur] = 1; pre[cur] = anc[cur] = dep;for (int i=0; i<n; ++i) if (edge[cur][i]) {if (i != father && 1 == vis[i]) {if (pre[i] < anc[cur])anc[cur] = pre[i];//back edge}if (0 == vis[i]) { //tree edgedfs(i,cur,dep+1,n);++cnt; // 分支个数if (anc[i] < anc[cur]) anc[cur] = anc[i];if ((cur==0 && cnt>1) ||(cnt!=0 && anc[i]>=pre[cur]))++deg[cur];// link degree of a vertex }}vis[cur] = 2;} /*==================================================*\| 最大团问题 DP + DFS| INIT: g[][]邻接矩阵;| CALL: res = clique(n);\*==================================================*/int g[V][V], dp[V], stk[V][V], mx;int dfs(int n, int ns, int dep){if (0 == ns) {if (dep > mx) mx = dep;return 1;}int i, j, k, p, cnt;for (i = 0; i < ns; i++) {k = stk[dep][i]; cnt = 0;if (dep + n - k <= mx) return 0;if (dep + dp[k] <= mx) return 0;for (j = i + 1; j < ns; j++) {p=stk[dep][j];if (g[k][p]) stk[dep + 1][cnt++] = p;}dfs(n, cnt, dep + 1);}return 1;}int clique(int n){int i, j, ns;for (mx = 0, i = n - 1; i >= 0; i--) {// vertex: 0 ~ n-1for (ns = 0, j = i + 1; j < n; j++)if (g[i][j]) stk[1][ ns++ ] = j;dfs(n, ns, 1); dp[i] = mx;}return mx;}/*==================================================*\| 欧拉路径O(E)| INIT: adj[][]置为图的邻接表; cnt[a]为a点的邻接点个数;| CALL: elpath(0); 注意:不要有自向边\*==================================================*/int adj[V][V], idx[V][V], cnt[V], stk[V], top;int path(int v){for (int w ; cnt[v] > 0; v = w) {stk[ top++ ] = v;w = adj[v][ --cnt[v] ];adj[w][ idx[w][v] ] = adj[w][ --cnt[w] ];// 处理的是无向图—-边是双向的,删除v->w后,还要处理删除w->v}return v;}void elpath (int b, int n){ // begin from b int i, j;for (i = 0; i < n; ++i) // vertex: 0 ~ n-1 for (j = 0; j < cnt[i]; ++j)idx[i][ adj[i][j] ] = j;printf("%d", b);for (top = 0; path(b) == b && top != 0; ) {b = stk[ --top ];printf("-%d", b);}printf("\n");}/*==================================================*\| Dijkstra数组实现O(N^2)| Dijkstra --- 数组实现(在此基础上可直接改为STL的Queue实现)| lowcost[] --- beg到其他点的最近距离| path[] -- beg为根展开的树,记录父亲结点\*==================================================*/#define INF 0x03F3F3F3Fconst int N;int path[N], vis[N];void Dijkstra(int cost[][N], int lowcost[N], int n, int beg){ int i, j, min;memset(vis, 0, sizeof(vis));vis[beg] = 1;for (i=0; i<n; i++){lowcost[i] = cost[beg][i]; path[i] = beg;}lowcost[beg] = 0;path[beg] = -1; // 树根的标记int pre = beg;for (i=1; i<n; i++){min = INF;dist[v] = dist[u] + c;for (j=0; j<n; j++)// 下面的加法可能导致溢出,INF 不能取太大if (vis[j]==0 &&lowcost[pre]+cost[pre][j]<lowcost[j]){lowcost[j] =lowcost[pre] + cost[pre][j]; path[j] = pre; } for (j=0; j<n; j++) if (vis[j] == 0 && lowcost[j] < min){ min = lowcost[j]; pre = j; } vis[pre] = 1; } } /*==================================================*\ | Dijkstra O(E * log E) | INIT: 调用init(nv, ne)读入边并初始化; | CALL: dijkstra(n, src); dist[i]为src 到i 的最短距离 \*==================================================*/ #define typec int // type of cost const typec inf = 0x3f3f3f3f; // max of cost typec cost[E], dist[V]; int e, pnt[E], nxt[E], head[V], prev[V], vis[V]; struct qnode { int v; typec c; qnode (int vv = 0, typec cc = 0) : v(vv), c(cc) {} bool operator < (const qnode& r) const { return c>r.c; } }; void dijkstra(int n, const int src){ qnode mv; int i, j, k, pre; priority_queue<qnode> que; vis[src] = 1; dist[src] = 0; que.push(qnode(src, 0)); for (pre = src, i=1; i<n; i++) { for (j = head[pre]; j != -1; j = nxt[j]) { k = pnt[j]; if (vis[k] == 0 && dist[pre] + cost[j] < dist[k]){ dist[k] =dist[pre] + cost[j]; que.push(qnode(pnt[j], dist[k])); prev[k] = pre; } } while (!que.empty() && vis[que.top().v] == 1) que.pop(); if (que.empty()) break ; mv = que.top(); que.pop(); vis[pre = mv.v] = 1; } } inline void addedge(int u, int v, typec c){ pnt[e] = v; cost[e] = c; nxt[e] = head[u]; head[u] = e++; } void init(int nv, int ne){ int i, u, v; typec c; e = 0;memset(head, -1, sizeof (head));memset(vis, 0, sizeof (vis));memset(prev, -1, sizeof (prev));for (i = 0; i < nv; i++) dist[i] = inf;for (i = 0; i < ne; ++i) {scanf("%d%d%d", &u, &v, &c);// %d: type of cost addedge(u, v, c); // vertex: 0 ~ n-1, 单向边 }}/*==================================================*\| BellmanFord 单源最短路O(VE)| 能在一般情况下,包括存在负权边的情况下,解决单源最短路径问题| INIT: edge[E][3]为边表| CALL: bellman(src);有负环返回0;dist[i]为src 到i 的最短距| 可以解决差分约束系统: 需要首先构造约束图,构造不等式时>=表示求最小值, 作为最长路,<=表示求最大值, 作为最短路 (v-u <= c:a[u][v] = c )\*==================================================*/#define typec int // type of costconst typec inf=0x3f3f3f3f; // max of costint n, m, pre[V], edge[E][3];typec dist[V];int relax (int u, int v, typec c){if (dist[v] > dist[u] + c) {pre[v] = u; return 1; } return 0; } int bellman (int src){ int i, j;for (i=0; i<n; ++i) { dist[i] = inf; pre[i] = -1; } dist[src] = 0; bool flag; for (i=1; i<n; ++i){ flag = false; // 优化 for (j=0; j<m; ++j) { if( 1 == relax(edge[j][0], edge[j][1], edge[j][2]) ) flag = true; } if( !flag ) break; } for (j=0; j<m; ++j) { if (1 == relax(edge[j][0], edge[j][1], edge[j][2])) return 0; // 有负圈 } return 1; } /*==================================================*\ | SPFA(Shortest Path Faster Algorithm) Bellman-Ford 算法的一种队列实现,减少了不必要的冗余计算。

02最小生成树

02最小生成树

范例:制造系统的分组技术
建模
设用Mi表示需由机器i加工的零件集,对任 意两台机器i,j, 定义相异度:
(i, j) | Mi M j | | Mi M j |
建模
(i, j) | Mi M j | | Mi M j |
“”:对称差, 分子:在机器i但不在机器j上加工,或在机 器j但不在机器i上加工的零件数。 分母:或在机器i,或在机器j上加工的零件数。 显然 01
最小生成树
一些常见的树形结构
无向树的定义
无向树: 连通无回路的无向图 平凡树: 平凡图 森林: 每个连通分支都是树的非连通的无向图 树叶: 树中度数为1的顶点 分支点: 树中度数2的顶点 例如
(a)
(b)
子图
定义设G=<V,E>, G=<V,E>是2个图(同为无向图,或同 为有向图) 若VV且EE, 则称G为G的子图, G为G的母图, 记作 GG 若GG 且V=V, 则称G为G的生成子图 设VV且V, 以V为顶点集, 以两端点都在V中的所有 边为边集的G的子图称作V的导出子图, 记作G[V] 设EE且E, 以E为边集, 以E中边关联的所有顶点为 顶点集的G的子图称作E的导出子图, 记作G[E]
范例:制造系统的分组技术
分组技术是设计制造系统的一种方法,它把生 产零件的机器分组,相应地把需生产的零件分类,使 零件跨组加工的情形尽量少,最理想的情况是使每个 零件的加工,都在组内完成。 假设有13种零件,需在9台机器上加工。在各台 机器上加工的零件号在下表中给出。
范例:制造系统的分组技术
机器 加工 的零 件 1 2,3, 7,8, 9,12 ,13 2 2,7, 8,11 ,12, 3 1,6 表 4 5 3,5, 3,7, 10 8,9, 12, 13 6 5 7 4,10 8 4,10 9 6

最小生成树和最短路径 -回复

最小生成树和最短路径 -回复

最小生成树和最短路径-回复什么是最小生成树和最短路径?如何确定它们?这两个概念通常在计算机科学中被广泛应用于解决图论中的相关问题。

在这篇文章中,我们将一步一步地回答这些问题。

首先,让我们来了解最小生成树是什么。

在图论中,最小生成树是一个连通无向图的生成树,其所有边的权重之和最小,并且包含该图的所有顶点。

生成树是一种树状结构,它是由图中所有的顶点以及它们之间的一些边组成,并且这些边必须满足以下条件:它们连接图中的不同顶点,并且不形成环。

为了更好地理解这个定义,让我们通过一个简单的例子来说明最小生成树的概念。

假设我们有一个城市网络,城市之间的路径可以用边来表示,边上的权重表示两个城市之间的距离。

现在我们的目标是建设一条最小的路径,连接这些城市,使得整个网络的总距离最小。

这条路径就是最小生成树。

那么如何确定最小生成树呢?在解决这个问题时,我们可以使用一些经典的算法,其中最著名的是普里姆算法和克鲁斯卡尔算法。

普里姆算法是一种贪心算法,在每一步中选择一个顶点并将其加入最小生成树中,然后选择一个连通该顶点的边权重最小的顶点,将其也加入最小生成树中。

这个过程会一直重复,直到所有的顶点都被添加到最小生成树中。

克鲁斯卡尔算法也是一种贪心算法,它首先将所有的边按权重进行排序,然后从最小权重的边开始,依次将边添加到最小生成树中,直到所有的顶点都被连接起来。

在添加每一条边时,需要判断是否会形成环,如果会形成环,则不选择该边。

当然,最小生成树不止有普里姆算法和克鲁斯卡尔算法这两种求解方法,还有其他一些算法,例如克鲁斯卡尔算法的变体Prim-Dijkstra算法和Boruvka算法等等。

每种算法都有其自身的特点和适用场景,根据具体的问题需求选择合适的算法进行求解。

接下来,让我们来了解最短路径是什么。

在一个加权有向图中,最短路径是指两个顶点之间的路径,其边的权重之和最小。

最短路径问题在计算机科学中有许多应用,例如导航系统、网络路由以及大规模数据处理等领域。

最小树与最小树形图(数学建模)讲解

最小树与最小树形图(数学建模)讲解

最小树与最小树形图(数学建模)讲解一、最小树的定义及性质1. 定义:最小树,又称最小树,是指在给定的带权无向图中,包含图中所有顶点的一个树形结构,且树中所有边的权值之和最小。

2. 性质:(1)最小树中不存在回路;(2)对于最小树中的任意两个顶点,它们之间有且仅有一条路径;(3)最小树中边的数量等于顶点数量减一;(4)在最小树中添加任意一条边,都会形成一条回路;(5)最小树不唯一,但权值之和相同。

二、求解最小树的方法1. Prim算法Prim算法是一种贪心算法,其基本思想是从图中的一个顶点开始,逐步添加边和顶点,直到形成最小树。

具体步骤如下:(1)初始化:选择一个顶点作为最小树的起点,将其加入最小树集合;(2)迭代:在最小树集合和非最小树集合之间,寻找一条权值最小的边,将其加入最小树集合;(3)重复步骤2,直到所有顶点都加入最小树集合。

2. Kruskal算法Kruskal算法同样是一种贪心算法,其基本思想是将图中的所有边按权值从小到大排序,然后依次选择权值最小的边,判断是否形成回路,若不形成回路,则将其加入最小树集合。

具体步骤如下:(1)初始化:将所有顶点视为独立的树;(2)按权值从小到大排序所有边;(3)迭代:选择权值最小的边,判断其是否形成回路,若不形成回路,则将其加入最小树集合;(4)重复步骤3,直到所有顶点都在同一棵树中。

三、最小树形图的定义及求解方法1. 定义:最小树形图,又称最优树形图,是指在给定的有向图中,找到一个包含所有顶点的树形结构,使得树中所有边的权值之和最小。

2. 求解方法:朱刘算法(Edmonds' Algorithm)朱刘算法是一种用于求解最小树形图的算法,其基本思想是通过寻找图中的最小权值环,进行收缩和扩展操作,最终得到最小树形图。

具体步骤如下:(1)寻找最小权值环;(2)对最小权值环进行收缩操作,将环中的顶点合并为一个新顶点;(3)在新图中寻找最小树形图;(4)将新图中的最小树形图扩展回原图,得到原图的最小树形图。

斯坦纳树算法

斯坦纳树算法

斯坦纳树算法
斯坦纳树算法,也称为“终极斯坦纳树算法”,是一种用于解决带权图中的最小树形图问题的算法。

该算法具有时间复杂度为O(n^3 * 2^n)的特点,因此适用于小型图和中等规模的图,但在大型图上会出现运行时间过长的问题。

斯坦纳树算法的基本思想是将原图中的顶点集合划分成若干个
子集,然后对每个子集构建一棵最小树形图,最后将所有子集的最小树形图合并成一棵最终的最小树形图。

该算法的核心在于如何构建子集的最小树形图,这通常使用DP(动态规划)的方式进行求解。

具体而言,斯坦纳树算法的步骤如下:
1.将所有边的权值取对数,并对原图做一些预处理,以提高算法效率。

2.对于每个子集S,使用DP的方式计算S中所有点到S中某个点的最短距离,记为d(S,v),其中v是S中任意一个点。

3.对于每个子集S,将S中所有点连成一棵生成树,使得该树的根节点为S中某个点,且所有连边的权值之和等于d(S,v)。

4.通过枚举所有的子集S,计算每个子集的最小树形图,并记录下最小树形图的权值。

5.将所有子集的最小树形图合并成一棵最终的最小树形图。

需要注意的是,斯坦纳树算法只适用于有向图,且存在一些限制条件,如原图必须联通等。

此外,该算法在实际应用中,可能需要结合其他算法一起使用,以解决一些特殊情况下的问题。

最小生成树(Kruskal和Prim算法)

最小生成树(Kruskal和Prim算法)

最⼩⽣成树(Kruskal和Prim算法)关于图的⼏个概念定义:关于图的⼏个概念定义:连通图:在⽆向图中,若任意两个顶点vi与vj都有路径相通,则称该⽆向图为连通图。

强连通图:在有向图中,若任意两个顶点vi与vj都有路径相通,则称该有向图为强连通图。

连通⽹:在连通图中,若图的边具有⼀定的意义,每⼀条边都对应着⼀个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通⽹。

⽣成树:⼀个连通图的⽣成树是指⼀个连通⼦图,它含有图中全部n个顶点,但只有⾜以构成⼀棵树的n-1条边。

⼀颗有n个顶点的⽣成树有且仅有n-1条边,如果⽣成树中再添加⼀条边,则必定成环。

最⼩⽣成树:在连通⽹的所有⽣成树中,所有边的代价和最⼩的⽣成树,称为最⼩⽣成树。

构造最⼩⽣成树的准则有3条:(1)必须只使⽤该⽹络中的边来构造最⼩⽣成树。

(2)必须使⽤且仅使⽤n-1条边来连接⽹络中的n个顶点。

(3)不能使⽤产⽣回路的边。

下⾯介绍两种求最⼩⽣成树算法1 Prim(普利姆算法)算法--加点法此算法可以称为“加点法”,每次迭代选择代价最⼩的边对应的点,加⼊到最⼩⽣成树中。

算法从某⼀个顶点s开始,逐渐长⼤覆盖整个连通⽹的所有顶点。

Prim算法从任意⼀个顶点开始,每次选择⼀个与当前顶点集最近的⼀个顶点,并将两顶点之间的边加⼊到树中。

Prim算法在找当前最近顶点时使⽤到了贪婪算法。

实现过程:5int logo[1010];///⽤0和1来表⽰是否被选择过6int map1[1010][1010];7int dis[1010];///记录任意⼀点到这⼀点的最近的距离8int n,m;9int prim()10 {11int i,j,now;12int sum=0;13for(i=1;i<=n;i++)///初始化14 {15 dis[i]=MAX;16 logo[i]=0;17 }18for(i=1;i<=n;i++)19 {20 dis[i]=map1[1][i];21 }22 dis[1]=0;23 logo[1]=1;24for(i=1;i<n;i++)///循环查找25 {26 now=MAX;27int min1=MAX;28for(j=1;j<=n;j++)29 {30if(logo[j]==0&&dis[j]<min1)31 {32 now=j;33 min1=dis[j];34 }35 }36if(now==MAX)///防⽌不成图37 {38break;39 }40 logo[now]=1;41 sum=sum+min1;42for(j=1;j<=n;j++)///填⼊新点后更新最⼩距离,到顶点集的距离43 {44if(logo[j]==0&&dis[j]>map1[now][j])45 {46 dis[j]=map1[now][j];47 }48 }49 }50if(i<n)51 {52 printf("?\n");53 }54else55 {56 printf("%d\n",sum);57 }58 }59int main()60 {61while(scanf("%d%d",&m,&n)!=EOF)///n是点数62 {63if(m==0)64 {65break;66 }67 memset(map1,0x3f3f3f3f,sizeof(map1));///map是邻接矩阵储存图的信息68for(int i=0;i<m;i++)69 {70int a,b,c;71 scanf("%d%d%d",&a,&b,&c);72if(c<map1[a][b])///防⽌出现重边73 {74 map1[a][b]=map1[b][a]=c;75 }76 }77 prim();78 }79return0;80 }邻接表实现:1 #include<stdio.h>2 #include<string.h>3 #include<vector>4 #include<algorithm>5#define INF 0x3f3f3f3f6using namespace std;7struct node8 {9int end;///终点10int power;///权值11 } t;12int n;///n为点数13 vector<node>q[500001];///邻接表储存图的信息14int dis[500001];///距离15int vis[500001];///标记数组16void prime()17 {18int i,len,j,pos,sum,start;19 memset(vis,0,sizeof(vis));20 sum=0;21 start=1;///任意取起点22for(i=0; i<=n; i++)23 {24 dis[i]=INF;25 }26 len=q[start].size();27for(i=0; i<len; i++)///从任意起点开始的dis数组更新28 {29if(q[start][i].power<dis[q[start][i].end])30 {31 dis[q[start][i].end]=q[start][i].power;32 }33 }34 vis[start]=1;35for(j=0; j<n-1; j++)36 {37int pos,min=INF;38for(i=1; i<=n; i++)39 {40if(vis[i]!=0&&dis[i]<min)41 {42 min=dis[i];43 pos=i;///找到未访问节点中权值最⼩的44 }45 }46if(pos==INF)///防⽌不成图47 {48break;49 }50 vis[pos]=1;51 sum=sum+min;52 len=q[pos].size();///再次更新dis数组53for(j=0; j<len; j++)54 {55if(vis[q[pos][j].end]==0&&dis[q[pos][j].end]>q[pos][j].power)56 {57 dis[q[pos][j].end] = q[pos][j].power;58 }59 }60 }61if(j<n)62 {63 printf("?\n");64 }65else66 {67 printf("%d\n",sum);68 }69 }70int main()71 {72int m,i;73int begin,end,power;74int a,b;75while(scanf("%d%d",&n,&m)!=EOF)76 {77for(i=0; i<=n; i++)78 {79 q[i].clear();///将victor数组清空80 }81for(i=0; i<m; i++)82 {83 scanf("%d%d%d",&begin,&end,&power);///输⼊84 t.end=end;85 t.power=power;86 q[begin].push_back(t);87 t.end=begin;///⽆向图88 t.power=power;89 q[end].push_back(t);90 }91 prime();92 }93return0;94 }这⾥再给出⼀个没有使⽤标记数组的代码:int prim(int s){int i,j,sum=0;int now;for(i=1;i<=n;i++){closest[i]=INT_MAX;}for(i=1;i<=n;i++){closest[i]=map[s][i];}closest[s]=0;for(i=1;i<n;i++)//这⾥的i代表的是边数,有n个点就会有n-1条边{int min=INT_MAX;for(j=1;j<=n;j++){if(closest[j]&&closest[j]<min){min=closest[j];now=j;//找到所需的最⼩边}}sum+=min;closest[now]=0;//将找到的边加⼊到最⼩⽣成树之中for(j=1;j<=n;j++)//找到新的点加⼊已选点集合之后,更新该点到未选点集合的距离{if(map[now][j]&&map[now][j]<closest[j]){closest[j]=map[now][j];}}}return sum;}2 Kruskal(克鲁斯卡尔)算法--加边法1.概览 Kruskal算法是⼀种⽤来寻找最⼩⽣成树的算法,在剩下的所有未选取的边中,找最⼩边,如果和已选取的边构成回路,则放弃,选取次⼩边。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
相关文档
最新文档