MST与贪心策略

6 篇文章 0 订阅
2 篇文章 0 订阅

定理

  • 割中最轻边(唯一)必在某个(全部)MST中(利用此结论可完成对Prim与Kruskal的证明)
  • 圈中最重边(唯一)必不在某个(全部)MST中
    • 证明:若在MST中,则去掉e后,MST被划分为X-Y,再取圈中另一可链接X-Y的边,加入MST中,立得一个更小的MST
  • 若所有圈/割的最重/轻边唯一 ⇒ \Rightarrow MST唯一,反之不成立
    • 证明即添加/删除最轻/重边,之后在证明这样构成的图是一棵树即可(容易证得连通无环)
  • (*)向一棵MST中任意加一边e,必构成一圈且e是圈中最大权。

Prim算法

  • 算法描述:
    • 记已选中点集为T,未选中点集为S,迭代的加入T-S距离最小的边
  • 正确性证明:
    • 法一
    • 只需证明任何一轮的生成树都满足(*)性质即可
    • 假设 T k − 1 满 足 ( ∗ ) 性 质 , 下 证 T k 也 满 足 ( ∗ ) 性 质 T_{k-1}满足(*)性质,下证T_k也满足(*)性质 Tk1()Tk()
      • T k 中 加 入 一 边 e , 若 e 在 T k − 1 中 , 则 命 题 成 立 , 若 e 不 在 T k − 1 T_k中加入一边e,若e在T_{k-1}中,则命题成立,若e不在T_{k-1} TkeeTk1eTk1中:
      • 则假定e不是圈中最大边,分别沿圈顺逆时针找到第一个大于e的两边e1,e2
      • 可以证明若Prim算法在之前先选择了e1,则接下来一定会先于e2选择e
      • 这与现在的G结构矛盾
      • 命题得证
    • 综上,Prim算法正确
    • 法二
    • 只需证明任何一个新加入的边都满足割中最小边即可(最后补证是树即可)
    • 显然,命题得证。(hhhhhh忘了这茬,我傻了)
  • 复杂度分析:
    • O ( ( n + m ) l o g n ) \Omicron((n+m)logn) O(n+m)logn

Kruskal算法

  • 算法描述:
    • 边排序,按序加边,并查集查环,无环则加
  • 复杂度分析: O ( m l o g m ) \Omicron(mlogm) O(mlogm)

Dijkstra框架

  • 证明相关
    • 数学归纳法:
      • 对Dijkstra的n的阶段进行数学归纳法证明,其中,第k个阶段表示从s到k个顶点的最短路都已确定
      • base:s到其自身的最短路长为0,显然成立
      • 若对于 ≤ k 均 成 立 : \le k均成立: k那么对于新被选中的第k+1个点z,我们设已经被选中的k个顶点构成点集S,则被选中路径经由S中一点m,我们需要证明其最短路就是我们选中的s->m->z:
      • 若不是s->m->z,
        • 假设经由S内另一点m2,使得s->m2->z小于s->m->z,但算法选中的是经由S中一点到达z最短的路径,所以这样的m2不存在
        • 假设经由S外一点m2使得s->m2->z小于s->m->z,但s->m2必然经由S内一点出去,记出边为e,则因为算法选中的是最短的,所以s->e大于s->m->z,而e->m2->z又大于等于0,所以s->e->m2->z大于等于s->m->z,矛盾
      • 所以,我们选中的z点正确。
      • 综上,Dijkstra框架证毕
    • 副产品:若从s点出发的边都是负权,则Disktra仍然实用(前提是无负圈)
    • 算法复杂度: O ( ( m + n ) l o g n ) 3 \Omicron((m+n)logn)3 O((m+n)logn)3

综合应用:

  • 输油管道问题
  • 附带点权最小问题
  • 油箱加油问题: w ( v ) = m a x ( w ( u ) , w ( u , v ) ) w(v)=max(w(u),w(u,v)) w(v)=max(w(u),w(u,v))
  • DAG求最短路:转置后拓扑排序即可

约定

  • 考试不考非图贪心
  • 贪心按照题意来即可,不必证明其正确性。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
贪心算法最小生成树是指通过不断地选择当前状态下的最优解,最终得到全局最优解的一种算法。在最小生成树问题中,我们需要找到一棵包含所有节点的树,使得树上边的权值之和最小。 以下是使用 Prim 算法实现最小生成树的 C 语言代码: ``` #include <stdio.h> #include <limits.h> #define V 5 //节点数 int minKey(int key[], bool mstSet[]) { int min = INT_MAX, min_index; for (int v = 0; v < V; v++) if (mstSet[v] == false && key[v] < min) min = key[v], min_index = v; return min_index; } void printMST(int parent[], int graph[V][V]) { printf("Edge \tWeight\n"); for (int i = 1; i < V; i++) printf("%d - %d \t%d \n", parent[i], i, graph[i][parent[i]]); } void primMST(int graph[V][V]) { int parent[V]; int key[V]; bool mstSet[V]; for (int i = 0; i < V; i++) key[i] = INT_MAX, mstSet[i] = false; key[0] = 0; parent[0] = -1; for (int count = 0; count < V - 1; count++) { int u = minKey(key, mstSet); mstSet[u] = true; for (int v = 0; v < V; v++) if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v]) parent[v] = u, key[v] = graph[u][v]; } printMST(parent, graph); } int main() { int graph[V][V] = { { 0, 2, 0, 6, 0 }, { 2, 0, 3, 8, 5 }, { 0, 3, 0, 0, 7 }, { 6, 8, 0, 0, 9 }, { 0, 5, 7, 9, 0 } }; primMST(graph); return 0; } ``` 在这个实现中,我们使用了一个 key 数组来存储每个节点到最小生成树的最小边权值,一个 mstSet 数组来标记每个节点是否已经加入最小生成树,以及一个 parent 数组来记录每个节点的父节点。在每次迭代中,我们选取当前 key 值最小的节点 u,将它标记为加入最小生成树,然后更新它所有的邻居节点的 key 值和 parent 数组。最终,我们遍历 parent 数组来输出最小生成树的边。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值