整理的算法模板合集: ACM模板


最小生成树的扩展应用

能用kruskal打死不用prim
kruskal是要把所有的边都遍历一遍

图论中的超级源点就比较常用,要时刻想到它

题目列表:

题目 算法
AcWing 1146. 新的开始 超级源点的应用
AcWing 1145. 北极通讯网络 贪心
AcWing 346. 走廊泼水节 最小生成树添边变成完成图
AcWing 1148. 秘密的牛奶运输 严格次小生成树

A、AcWing 1146. 新的开始(超级源点的应用)

想办法将多模式的问题转换为单模式的问题,再利用求解该模式的算法解决即可

对于本题来说,答案有两个因素影响,一个是建发电站(结点权值),一个是连边(边权值),没办法用一个统一的的算法求解,因此我们要想办法将二者转换为一种东西。我们发现建发电站和连边,同样都是为一个没有电源的结点给予电源(连边也就是从有电源的结点传到没有电源的结点上)。因此我们可以很自然地想到,建立一个超级源点,这样每次为一个结点建立发电站同样可以看作从超级源点向欲要建发电站的结点连一条边,这样就转换为了普通两个结点的连边的操作。这样整个题目就只有连最小权值边使得整张图(n+1个点,原来n个点和新增的超级源点)全部联通的问题了,那么我们直接求一个最小生成树即可得到答案。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>using namespace std;const int N = 5007, M = 510007, INF = 0x3f3f3f3f;int n,m;
int g[N][N];
bool vis[N];
int dist[N];int prim(){memset(dist,0x3f,sizeof dist);dist[0] = 0;int res = 0;for(int i = 0;i < n + 1;++i){int t = -1;for(int j = 0;j < n + 1;++j)if(!vis[j] && ( t == -1 || dist[j] < dist[t]))t = j;vis[t] = true;res += dist[t];for(int j = 0;j < n + 1; ++ j)dist[j] = min(dist[j],g[t][j]);}return res;
}int main(){scanf("%d",&n);for(int i = 1; i <= n;++i){scanf("%d",&g[0][i]);g[i][0] = g[0][i];}for(int i = 1;i <= n;++i)for(int j = 1;j <= n;++j)scanf("%d",&g[i][j]);printf("%d\n",prim());return 0;
}

B、AcWing 1145. 北极通讯网络(贪心)

相当于有k个是免费的,直接求一个最小生成树,贪心地把权值最大的k条边免费,也就是从小到大,过n-k条,剩余k条时break,这时的边就是答案要求的最大覆盖范围d。
用y总的话就是:


当然这道题是满足单调性的,我们很容易就想到了二分。但是本题和关押罪犯那道题一样,直接用并查集做就可以省略二分了,因为我们直接贪心地从小到大,总是可以得到答案的。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>#define x first
#define y secondusing namespace std;
typedef pair<int,int> PII;const int N = 5007, M = 510007, INF = 0x3f3f3f3f;int n,m;
struct node{int u,v;double w;bool operator<(const node &t)const {return w < t.w;}
}e[M];
PII po[N];
int k;
int fa[N];double get_dist(PII a,PII b){int xx = a.x - b.x;int yy = a.y - b.y;return sqrt(xx * xx + yy * yy);
}int Find(int x)
{if (fa[x] != x) fa[x] = Find(fa[x]);return fa[x];
}int main(){cin>>n>>k;for(int i = 1;i <= n; ++ i)cin>>po[i].x>>po[i].y;for(int i = 1;i <= n;++i)for(int j = 1;j <= n;++j)e[++m] = {i,j,get_dist(po[i],po[j])};sort(e + 1,e + 1 + m);for(int i = 1;i <= n;++i)fa[i] = i;int cnt = n;double res = 0;for(int i = 1;i <= m;++i){if(cnt <= k)break;int a = Find(e[i].u),b = Find(e[i].v);double w = e[i].w;if(a != b){fa[a] = b;cnt--;res = w;}}printf("%.2f\n",res);return 0;
}

C、AcWing 346. 走廊泼水节(最小生成树生成完成图)

注意: 树中的所有边权均为整数,且新加的所有边权也必须为整数。

完全图是一个简单的无向图,其中每对不同的顶点之间都恰连有一条边相连。——百度百科

由于要求形成完全图依然还是这个最小生成树,所以我们所有新生成的两个点之间的边不能小于原来这两个点的直接相连的最小生成树里的边,不然求最小生成树的时候就会用新增的边了。由于所有的边权都是整数,所以每一个新增的边都应该为原来的权值w+1。然后由于生成的是完全图,所以每一对点都应该有一条边直接相连,也就是说每次用kruskal算法使得两个连通块相连的时候答案乘上两个连通块大小相乘再减去原有的生成树的边,也就是减一。因为相当于所有的点之间都连上一个权值为w+1的边。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define lowbit(x) x&(-x)
using namespace std;const int N = 200007;
typedef long long ll;int n,m;
struct node{int u,v,w;bool operator<(const node &t)const {return w < t.w;}
}e[N];
int fa[N];
int Size[N];int Find(int x){if(fa[x] == x)return x;return fa[x] = Find(fa[x]);
}
int t;
int main(){cin>>t;while(t--){scanf("%d",&n);for(int i = 0;i < n - 1;++i){int x,y,z;scanf("%d%d%d",&x,&y,&z);e[i] = {x,y,z};}long long res = 0;sort(e,e + n - 1);for(int i= 1;i <= n;++i)fa[i] = i,Size[i] = 1;for(int i = 0;i < n - 1;++i){int x = Find(e[i].u),y = Find(e[i].v);int z = e[i].w;if(x == y)continue;res += (ll)(z + 1) * (Size[x] * Size[y] - 1);fa[x] = y;Size[y] += Size[x];}cout<<res<<endl;}return 0;
}

D、AcWing 1148. 秘密的牛奶运输(严格次小生成树)

次小生成树有两种,一种是不严格次小生成树,也就是可以数值上等于最小生成树,一种是严格次小生成树,是权值严格大于最小生成树,两种求法大同小异。

方法2在严格次小生成树和不严格次小生成树都成立,而方法1只在严格次小生成树中成立

对于任意一颗最小生成树,都存在一个次小生成树(严格或者不严格都成立),与最小生成树只差1条边。

根据存在次小生成树与最小生成树只差一条边。
我们可以先通过kruskal求出最小生成树 O(mlogm)
通过这个树初始化某点到其他点的路径中最大边、次大边。 O(n2)
遍历所有未在树中的边(u–>v), 看是否大于u到v的边的最大值, 若大于那么可以替换这条边,若不大于那就再判断是否大于次值,若大于同理进行替换。因为这里的w一定是不小于最小生成树里的任何一条边的权值,最多会等于,而本题为严格次小生成树,不能等于。

然后本题实际上是整棵最小生成树自建立以来就没有动过,是典型的只要答案不干事,只是全程取min要答案,这样就保证了无后效性和算法的正确性。

本题的时间复杂度为O(mlogm+n2+mlogn)O(mlogm + n^2+mlogn)O(mlogm+n2+mlogn)
算法瓶颈为dfs查找最大值和次大值,想这一道要求O(mlogn)O(mlogn)O(mlogn)过的P4180 [BJWC2010]严格次小生成树就只能拿50分。
我们可以优化dfs操作,用LCA和倍增代替,可以把时间压缩到O(mlogn)O(mlogn)O(mlogn)
这道明天更新

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>using namespace std;typedef long long ll;
const int N = 507,M = 1e4 + 7;int n,m;
int dist1[N][N],dist2[N][N];//从i到j的路径上的一条最大权值边和次大权值边int fa[N];
struct node{int u,v,w;bool flag;bool operator<(const node &t)const {return w < t.w;}
}e[M];int head[N],edge[M],nex[M],ver[M],tot = 1;void add(int x,int y,int z){ver[++tot] = y;edge[tot] = z;nex[tot] = head[x];head[x] = tot;
}int Find(int x){if(fa[x] == x)return x;return fa[x] = Find(fa[x]);
}void dfs(int u,int father,int max1,int max2,int d1[],int d2[]){//这样传进来的数组就只是第二维的,就变成一维数组了d1[u] = max1;d2[u] = max2;for(int i = head[u];~i;i = nex[i]){int v = ver[i],w = edge[i];if(v != father){int t1 = max1,t2 = max2;if(w > t1)//如果大于当前最大值,最大值给次大值,然后再更新最大值t2 = t1,t1 = w;else if(w < t1 && w > t2)t2 = w;dfs(v,u,t1,t2,d1,d2);}}
}int main(){memset(head,-1,sizeof head);scanf("%d%d",&n,&m);for(int i = 1;i <= m;++i){int x,y,z;scanf("%d%d%d",&x,&y,&z);e[i] = {x,y,z};}sort(e + 1,e + m + 1);for(int i = 1;i <= n;++i)fa[i] = i;ll sum = 0;for(int i = 1;i <= m;++i){int x = e[i].u,y = e[i].v;int fx = Find(x),fy = Find(y);int z = e[i].w;if(fx == fy)continue;sum += z;add(x,y,z),add(y,x,z);//建成一颗树fa[fx] = fy;e[i].flag = true;//表示是树边}for(int i = 1;i <= n;++i)//处理一下所有点的最大值和次大值dfs(i,-1,0,0,dist1[i],dist2[i]);ll res = 1e18;for(int i = 1;i <= m;++i)if(!e[i].flag){int w = e[i].w;int u = e[i].u,v = e[i].v;if(w > dist1[u][v])res = min(res,sum - dist1[u][v] + w);else if(w > dist2[u][v])res = min(res,sum - dist2[u][v] + w);}printf("%lld\n",res);return 0;
}

【图论专题】最小生成树的扩展应用相关推荐

  1. .[算法]图论专题之最短路径

    .[算法]图论专题之最短路径 作者:jasonkent27 转载请注明出处:www.cnblogs.com/jasonkent27 1. 前言 1.1 最短路引入 小明和小天现在住在海口(C1),他们 ...

  2. 图论专题训练 (更新中)

    图论专题训练 A 题意: 一个国家里有很多个城市,某件物品在所有城市的价格都不同,你可以在一个城市买,另一个城市卖出来获得利益,但是只能进行一次买卖.然后要从1走到n,1到n有单向,也有双向的. 题解 ...

  3. 《程序设计解题策略》——1.2 利用最小生成树及其扩展形式解题

    本节书摘来自华章计算机<程序设计解题策略>一书中的第1章,第1.2节,作者:吴永辉 王建德 更多章节内容可以访问云栖社区"华章计算机"公众号查看. 1.2 利用最小生成 ...

  4. 图论专题-学习笔记:强连通分量

    图论专题-学习笔记:强连通分量 一些 update 1. 前言 2. 定义 3. 求法 4. 应用 5. 总结 一些 update update on 2021/8/12:增加了对于 Kosaraju ...

  5. 图论专题-学习笔记:虚树

    图论专题-学习笔记:虚树 1. 前言 2. 详解 2.1 虚树定义 2.2 虚树构造 2.3 例题 3. 总结 4. 参考资料 1. 前言 虚树,主要是用于一类树上问题,这类问题通常是需要取一些关键点 ...

  6. 【图论专题】最小生成树及其简单应用

    整理的算法模板合集: ACM模板 题目列表: 题目 算法 AcWing 1140. 最短网络 prim模板 AcWing 1141. 局域网 kruskal模板 AcWing 1142. 繁忙的都市 ...

  7. 【图论专题】Floyd算法及其扩展应用

    Floyd的拓展应用: 任意两点最短路 传递闭包 找最小环 恰好经过k条边的最短路(倍增) 题目列表: 题目 算法 AcWing 1125. 牛的旅行 任意两点最短路Floyd AcWing 343. ...

  8. 【图论专题】单源最短路的扩展应用

    题目列表: 题目 算法 AcWing 1137. 选择最佳线路 最短路+超级源点 AcWing 1131. 拯救大兵瑞恩 拆点建图+思维+双端队列BFS+状态压缩 AcWing 1134. 最短路计数 ...

  9. 图论专题1(网络流)

    推荐阅读: 网络流基础知识和Dinic:http://www.cnblogs.com/SYCstudio/p/7260613.html#3848907 建模:https://www.cnblogs.c ...

最新文章

  1. MySQL主主(双主)数据同步
  2. 程序猿小白应该注意什么
  3. Vue 中的compile操作方式
  4. 理解统计信息(3/6):谁创建和管理统计信息?在性能调优中,统计信息的作用。...
  5. Linux KVM 虚拟化技术
  6. Codeforces Round #250 (Div. 2) A - The Child and Homework
  7. 用一个按钮做主窗口,可以吗?
  8. 【数据库系统】数据库引入空值null的意义
  9. C语言刷抖音源码,iOS多种刷新样式、音乐播放器、仿抖音视频、旅游App等源码...
  10. Threading.Timer用法
  11. PHP随机生成中国人姓名的类
  12. python发音模块-python 利用pyttsx3文字转语音过程详解
  13. Flutter关于简单的吸顶通讯录制作
  14. 百度钱包 java_百度钱包安全控件
  15. K8S的pod探针(livenessProbe,readinessProbe),kubelet对pod的状态检查(kubelet-exec,httpGet,tcpSocket)
  16. [转载]Wifi OKC 验证
  17. 关于未分摊差异的几种处理办法
  18. 高仿拼多多源码/拼单商城系统源码/拼团商城源码
  19. LaSO: Label-Set Operations networks for multi-label few-shot learning 论文笔记
  20. 苹果动态壁纸库怎么增加_苹果xr如何添加动态壁纸设置

热门文章

  1. Pytorch:使用DCGAN实现数据复制
  2. Python 之父为什么嫌弃 lambda 匿名函数?
  3. 使用Python中的OpenCV降噪功能增强图像的3个步骤
  4. 你必须尝试的20个 Python 库
  5. 基于OpenCV的焊件缺陷检测
  6. Golang正则笔记 :使用正则表达式处理题库文本
  7. TODO:macOS编译PHP7.1
  8. Codeforces Round #319 (Div. 1) B. Invariance of Tree 构造
  9. 【JQuery】可直接编辑的表格
  10. 企业创新系列之:青苹之末