#include<bits/stdc++.h>usingnamespace std;signedmain(){longlong n;
cin >> n;if(n &1)return cout <<0<<'\n',0;// 如果 n 是奇数,末尾一定没有零longlong ans = n /10;// 先将 n 除以 10,统计其中的 2 的个数
n /=10;longlong e =5;while(e <= n){// 统计其中的 5 的个数
ans += n / e;
e *=5;}
cout << ans <<'\n';return0;}
#include<bits/stdc++.h>// 使用标准命名空间,这样可以直接使用标准库中的函数和对象usingnamespace std;// 定义长整型别名 ll,方便后续使用using ll =longlong;// 定义常量 N,表示图中节点的最大数量constint N =405;// 定义常量 inf,表示无穷大,用于初始化距离矩阵const ll inf =2e18;// 定义二维数组 d,用于存储任意两点之间的最短距离
ll d[N][N];/*
这段代码实现了使用 Floyd-Warshall 算法来计算图中任意两点之间的最短路径,
并处理了多个查询,查询两点之间的最短距离。
如果两点之间不存在路径,则输出 -1。
*/intmain(){// 读取输入的节点数量 n、边的数量 m 和查询数量 qint n,m,q;
cin >> n >> m >> q;// 初始化距离矩阵 d,将任意两点之间的距离初始化为无穷大for(int i =1; i <= n; i++)for(int j =1; j <= n; j++)
d[i][j]= inf;// 任意节点到自身的距离为 0for(int i =1; i <= n; i++)
d[i][i]=0;// 读取每条边的信息while(m--){// 读取边的起点 u、终点 v 和边的权重 w
ll u, v, w;
cin >> u >> v >> w;// 更新距离矩阵 d,取当前存储的距离和新边权重的最小值
d[u][v]=min(d[u][v], w);
d[v][u]=min(d[v][u], w);}// 使用 Floyd-Warshall 算法计算任意两点之间的最短距离for(int k =1; k <= n; k++)for(int i =1; i <= n; i++)for(int j =1; j <= n; j++)// 更新距离矩阵 d,取当前存储的距离和经过中间节点 k 的距离的最小值
d[i][j]=min(d[i][j], d[i][k]+ d[k][j]);// 处理查询while(q--){// 读取查询的起点 st 和终点 edint st, ed;
cin >> st >> ed;// 如果起点到终点的距离仍为无穷大,说明两点之间不存在路径,输出 -1// 否则输出最短距离
cout <<(d[st][ed]== inf ?-1: d[st][ed])<< endl;}return0;}
蓝桥王国(单源最短路,邻接表,dijk)
问题描述
代码
#include<bits/stdc++.h>usingnamespace std;// 定义长整型别名 ll,方便后续使用长整型数据using ll =longlong;// 定义常量 N,作为数组的最大长度,用于存储图的节点信息const ll N =3e5+10;// 定义常量 inf,代表无穷大,用于初始化距离const ll inf =2e18;// 定义结构体 node,用于存储节点编号 u 和到该节点的距离 disstructnode{
ll u, dis;// 重载小于运算符,用于优先队列的比较// 这里将距离大的元素优先级设为低,实现小顶堆的效果booloperator<(const node& x)const{return dis > x.dis;}};// 数组 d 用于存储从起点到各个节点的最短距离
ll d[N];// 邻接表 a,用于存储图的结构,a[u] 存储从节点 u 出发能到达的节点及其距离
vector<node> a[N];// 图的节点数量 n 和边的数量 mint n, m;// 数组 vis 用于标记节点是否已经被访问过bool vis[N];// Dijkstra 算法函数,用于计算从起点 st 到各个节点的最短距离voiddijk(ll st){// 初始化距离数组 d,将所有节点的距离设为无穷大for(int i =1; i <= n; i++){
d[i]= inf;}// 初始化访问标记数组 vis,将所有节点标记为未访问memset(vis,false,sizeof vis);// 定义优先队列 pq,用于存储待处理的节点,优先处理距离小的节点
priority_queue<node> pq;// 将起点 st 加入优先队列,起点到自身的距离为 0
pq.push({st, d[st]=0});// 当优先队列不为空时,继续处理while(pq.size()){// 取出优先队列中距离最小的节点
node tmp = pq.top();
pq.pop();// 获取该节点的编号
ll u = tmp.u;// 如果该节点已经被访问过,跳过if(vis[u])continue;// 标记该节点为已访问
vis[u]=true;// 遍历从节点 u 出发能到达的所有节点for(int i =0; i < a[u].size(); i++){// 获取能到达的节点编号
ll y = a[u][i].u;// 获取从节点 u 到节点 y 的距离
ll dy = a[u][i].dis;// 如果经过节点 u 到达节点 y 的距离更短if(d[u]+ dy < d[y]){// 更新节点 y 的最短距离
d[y]= d[u]+ dy;// 将更新后的节点 y 加入优先队列
pq.push({y, d[y]});}}}}intmain(){// 读取图的节点数量 n 和边的数量 m
cin >> n >> m;// 循环读取每条边的信息while(m--){// 读取边的起点 u、终点 v 和边的权重 w
ll u, v, w;
cin >> u >> v >> w;// 将边的信息加入邻接表 a 中
a[u].push_back({v, w});}// 调用 Dijkstra 算法,计算从节点 1 到各个节点的最短距离dijk(1);// 输出从节点 1 到各个节点的最短距离for(int i =1; i <= n; i++){// 如果某个节点的距离仍为无穷大,说明无法到达,输出 -1// 否则输出最短距离
cout <<(d[i]>= inf ?-1: d[i])<<" ";}return0;}
混境之地1
问题描述
输入
551125...#...#..
#...#
...#......212531131527
输出
2
样例解释
代码
#include<bits/stdc++.h>usingnamespace std;// 定义 x 为 pair 的第一个元素,y 为 pair 的第二个元素,方便后续使用#definexfirst#defineysecond// 定义 PII 为 pair<int, int> 类型,用于表示二维坐标typedef pair<int,int> PII;// 定义 PIII 为 pair<int, PII> 类型,用于优先队列中存储距离和坐标typedef pair<int,PII> PIII;// 定义常量 N 为 50,作为数组的最大大小constint N =50;// n 表示地图的行数,m 表示地图的列数int n,m;// sx, sy 表示起点的坐标,ex, ey 表示终点的坐标int sx,sy,ex,ey;// k 表示传送门的数量,E 表示拥有的能量值int k,E;// p 数组用于存储每个传送门的消耗能量int p[N][N];// port 数组用于存储每个传送门的目标坐标
PII port[N][N];// mp 数组用于存储地图信息char mp[N][N];// dist 数组用于存储从起点到每个点的最短距离int dist[N][N];// dx 和 dy 数组用于表示上下左右四个方向的偏移量int dx[]={0,1,0,-1};int dy[]={1,0,-1,0};// vis 数组用于标记某个点是否已经被访问过bool vis[N][N];// 使用 Dijkstra 算法计算从起点到终点的最短路径intdijkstra(){// 定义一个优先队列,按照距离从小到大排序
priority_queue<PIII, vector<PIII>,greater<PIII>> pq;// 将起点加入优先队列,初始距离为 0
pq.push({0,{sx,sy}});// 将 dist 数组初始化为一个很大的值,表示无穷大memset(dist,0x3f,sizeof dist);// 起点到自身的距离为 0
dist[sx][sy]=0;// 当优先队列不为空时,继续循环while(pq.size()){// 取出优先队列中距离最小的元素auto tt = pq.top();
pq.pop();// 取出该元素的坐标auto t = tt.y;// 如果该点已经被访问过,跳过if(vis[t.x][t.y])continue;// 标记该点为已访问
vis[t.x][t.y]=true;// 遍历上下左右四个方向for(int i =0; i <4; i++){// 计算下一个点的坐标int nx = t.x + dx[i], ny = t.y + dy[i];// 如果下一个点超出地图范围,跳过if(nx <1|| nx > n || ny <1|| ny > m)continue;// 如果下一个点是障碍物,跳过if(mp[nx][ny]=='#')continue;// 如果通过当前点到达下一个点的距离更短,更新距离if(dist[nx][ny]> dist[t.x][t.y]+1){
dist[nx][ny]= dist[t.x][t.y]+1;// 将更新后的点加入优先队列
pq.push({dist[nx][ny],{nx,ny}});}}// 如果当前点有传送门if(p[t.x][t.y]){// 取出传送门的目标坐标auto tt = port[t.x][t.y];int tx = tt.x, ty = tt.y;// 如果通过传送门到达目标点的距离更短,更新距离if(dist[tx][ty]> dist[t.x][t.y]+ p[t.x][t.y]){
dist[tx][ty]= dist[t.x][t.y]+ p[t.x][t.y];// 将更新后的点加入优先队列
pq.push({dist[tx][ty],{tx,ty}});}}}// 返回从起点到终点的最短距离return dist[ex][ey];}intmain(){// 输入地图的行数和列数
cin >> n >> m;// 输入起点和终点的坐标
cin >> sx >> sy >> ex >> ey;// 输入地图信息for(int i =1; i <= n; i++) cin >> mp[i]+1;// 输入传送门的数量
cin >> k;// 循环输入每个传送门的信息while(k--){int a,b,c,d,pk;
cin >> a >> b >> c >> d >> pk;// 记录传送门的起点和终点坐标
port[a][b]={c,d};// 记录传送门的消耗能量
p[a][b]= pk;}// 输入拥有的能量值
cin >> E;// 调用 Dijkstra 算法计算最短路径int res =dijkstra();// 如果能量值不足以到达终点,输出 -1if(E < res) cout <<-1<< endl;// 否则输出剩余的能量值else cout << E - res << endl;return0;}
混镜之地3
问题描述
输入
551155...#...#..
#..CC
...#C
...#.9
输出
Yes
输入输出样例解释
代码
#include<bits/stdc++.h>usingnamespace std;// 定义 x 为 pair 的第一个元素,y 为 pair 的第二个元素,方便后续使用#definexfirst#defineysecond// 定义 PII 为 pair<int, int> 类型,用于表示二维坐标typedef pair<int,int> PII;// 定义 PIII 为 pair<int, PII> 类型,用于优先队列中存储距离和坐标typedef pair<int,PII> PIII;// n 表示地图的行数,m 表示地图的列数,E 表示拥有的能量int n,m,E;// sx, sy 表示起点的坐标,ex, ey 表示终点的坐标int sx,sy,ex,ey;// mp 数组用于存储地图信息char mp[1005][1005];// dist 数组用于存储从起点到每个点的最短距离int dist[1005][1005];// dx 和 dy 数组用于表示上下左右四个方向的偏移量int dx[]={0,1,0,-1};int dy[]={1,0,-1,0};// vis 数组用于标记某个点是否已经被访问过bool vis[1005][1005];// 使用 Dijkstra 算法计算从起点到终点的最短路径intdijkstra(){// 定义一个优先队列,按照距离从小到大排序
priority_queue<PIII,vector<PIII>,greater<PIII>> pq;// 将 dist 数组初始化为一个很大的值,表示无穷大memset(dist,0x3f,sizeof dist);// 将起点加入优先队列,初始距离为 0
pq.push({0,{sx,sy}});// 起点到自身的距离为 0
dist[sx][sy]=0;// 当优先队列不为空时,继续循环while(pq.size()){// 取出优先队列中距离最小的元素auto tt = pq.top();
pq.pop();// 取出该元素的坐标auto t = tt.y;// 如果该点已经被访问过,跳过if(vis[t.x][t.y])continue;// 标记该点为已访问
vis[t.x][t.y]=true;// 遍历上下左右四个方向for(int i =0; i <4; i++){// 计算下一个点的坐标int nx = t.x + dx[i], ny = t.y + dy[i];// 如果下一个点超出地图范围或者是障碍物,跳过if(nx <=0|| nx > n || ny <=0|| ny > m || mp[nx][ny]=='#')continue;// 计算移动到下一个点的代价// 如果是 '.' 则代价为 0,否则为字符对应的数值('A' 对应 1,'B' 对应 2,以此类推)int w =(mp[nx][ny]=='.'?0: mp[nx][ny]-'A'+1);// 如果通过当前点到达下一个点的距离更短,更新距离if(dist[nx][ny]> dist[t.x][t.y]+ w){
dist[nx][ny]= dist[t.x][t.y]+ w;// 将更新后的点加入优先队列
pq.push({dist[nx][ny],{nx,ny}});}}}// 返回从起点到终点的最短距离return dist[ex][ey];}intmain(){// 输入地图的行数和列数
cin >> n >> m;// 输入起点和终点的坐标
cin >> sx >> sy >> ex >> ey;// 输入地图信息for(int i =1; i <= n; i++) cin >> mp[i]+1;// 输入拥有的能量
cin >> E;// 调用 Dijkstra 算法计算最短路径int res =dijkstra();// 如果能量不足以到达终点,输出 "No"if(E < res) cout <<"No"<< endl;// 否则输出 "Yes"else cout <<"Yes"<< endl;return0;}
最短路问题
问题描述
输入
551223344551
输出
2-11220-1
代码
#include<bits/stdc++.h>usingnamespace std;// 使用 ll 作为 long long 类型的别名,方便后续使用using ll =longlong;// 定义常量 N,用于表示数组的最大大小constint N =200+10;// d 数组用于存储图中任意两点之间的最短距离int d[N][N];// n 表示图中节点的数量,q 表示查询的数量int n, q;// 计算两个数 a 和 b 的最大公约数intgcd(int a,int b){// 如果 b 为 0,则 a 就是最大公约数,否则递归调用 gcd 函数return b ==0? a :gcd(b, a % b);}// 计算两个数 a 和 b 的最小公倍数intlcm(int a,int b){// 根据公式 lcm(a, b) = a * b / gcd(a, b) 计算最小公倍数return a /gcd(a, b)* b;}// 判断一个数 n 是否为质数boolisprime(int n){// 质数定义为大于 1 且只能被 1 和自身整除的数if(n <2)returnfalse;// 从 2 到 sqrt(n) 检查是否存在能整除 n 的数for(int i =2; i <=sqrt(n); i++){if(n % i ==0)returnfalse;}returntrue;}// 使用 Floyd-Warshall 算法计算图中任意两点之间的最短路径voidfloyd(){// 中间节点 k 从 1 到 n 遍历for(int k =1; k <= n; k++){// 起点 i 从 1 到 n 遍历for(int i =1; i <= n; i++){// 终点 j 从 1 到 n 遍历for(int j =1; j <= n; j++){// 更新 d[i][j] 为 d[i][j] 和 d[i][k] + d[k][j] 中的较小值
d[i][j]=min(d[i][j], d[i][k]+ d[k][j]);}}}}intmain(){// 输入节点数量 n 和查询数量 q
cin >> n >> q;// 将 d 数组初始化为一个很大的值,表示两点之间没有路径memset(d,0x3f,sizeof d);// 初始化邻接矩阵for(int i =1; i <= n; i++){for(int j = i +1; j <= n; j++){// 如果 i 和 j 中只有一个是质数if(isprime(i)+isprime(j)==1){// 则 i 到 j 的距离为它们的最小公倍数
d[i][j]=lcm(i, j);// 由于是无向图,j 到 i 的距离也为它们的最小公倍数
d[j][i]=lcm(i, j);}}}// 调用 Floyd-Warshall 算法计算任意两点之间的最短路径floyd();// 处理 q 次查询while(q--){int x, y;// 输入查询的起点 x 和终点 y
cin >> x >> y;// 如果 d[x][y] 仍然为初始的很大值,说明 x 到 y 没有路径if(d[x][y]==0x3f3f3f3f) cout <<-1<< endl;else cout << d[x][y]<< endl;}return0;}
会面
问题描述
输入
441212323434111423
输出
3
代码
#include<bits/stdc++.h>usingnamespace std;// 定义常量 N,用于表示数组的最大大小,这里 2e2 表示 200,再加 10 是为了防止越界constint N =2e2+10;// n 表示图中的节点数量,m 表示图中的边数量int n, m;// d 数组用于存储图中任意两点之间的最短距离,是一个二维数组int d[N][N];// s1 和 e1 分别表示第一个人的起点和终点,s2 和 e2 分别表示第二个人的起点和终点int s1, e1, s2, e2;intmain(){// 输入图的节点数量 n 和边的数量 m
cin >> n >> m;// 将 d 数组初始化为一个很大的值(0x3f 表示十六进制的 3f),用于表示两点之间没有路径memset(d,0x3f,sizeof d);// 任何节点到自身的距离为 0for(int i =1; i <= n; i++) d[i][i]=0;// 循环 m 次,每次输入一条边的信息while(m--){int a, b, t;// 输入边的两个端点 a 和 b,以及边的权重 t
cin >> a >> b >> t;// 更新 a 到 b 的最短距离,如果当前边的权重更小,则更新
d[a][b]=min(d[a][b], t);// 由于是无向图,b 到 a 的最短距离也更新
d[b][a]=min(d[b][a], t);}// 使用 Floyd-Warshall 算法计算图中任意两点之间的最短路径for(int k =1; k <= n; k++)for(int i =1; i <= n; i++)for(int j =1; j <= n; j++)// 更新 d[i][j] 为 d[i][j] 和 d[i][k] + d[k][j] 中的较小值
d[i][j]=min(d[i][j], d[i][k]+ d[k][j]);// 输入第一个人的起点 s1、终点 e1,以及第二个人的起点 s2、终点 e2
cin >> s1 >> e1 >> s2 >> e2;// 初始化结果为一个很大的值,用于后续比较int res =0x3f3f3f3f;// 枚举所有可能的中间节点 kfor(int k =1; k <= n; k++){// 计算以 k 为中间节点时,两人从起点到 k 再到终点的最大距离// 取两人从起点到 k 的最大距离,加上两人从 k 到终点的最大距离
res =min(res,max(d[s1][k], d[s2][k])+max(d[k][e1], d[k][e2]));}// 如果结果仍然是初始的很大的值,说明没有可行的路径,输出 -1,否则输出结果
cout <<(res ==0x3f3f3f3f?-1: res)<< endl;return0;}
慈善晚会
问题描述
输入
472132344423147124235312
输出
12
代码
#include<bits/stdc++.h>usingnamespace std;// 定义常量 N,作为数组的最大长度,1e3 表示 1000,加 10 是为避免边界问题constint N =1e3+10;// 定义 x 为 pair 的第一个元素,y 为 pair 的第二个元素,方便后续代码使用#definexfirst #defineysecond// 定义 PII 为 pair<int, int> 类型,用于存储距离和节点编号typedef pair<int,int> PII;// g 数组存储正向图的邻接表,h 数组存储反向图的邻接表
vector<PII> g[N], h[N];// d1 数组存储从点 p 到其他点的最短距离,vis1 数组用于标记点是否在正向 Dijkstra 中被访问过// d2 数组存储从其他点到点 p 的最短距离,vis2 数组用于标记点是否在反向 Dijkstra 中被访问过int d1[N], vis1[N], d2[N], vis2[N];// n 表示节点数量,m 表示边的数量,p 表示指定的节点int n, m, p;// 实现 Dijkstra 算法,计算最短路径// d 数组用于存储最短距离,fl 用于区分是正向图还是反向图,vis 数组用于标记节点是否被访问过voiddijkstra(int d[],int fl,int vis[]){// vec 数组用于存储当前使用的图(正向或反向)
vector<PII> vec[N];for(int i =1; i <= n; i++){// 如果 fl 为 1,使用正向图 gif(fl ==1) vec[i]= g[i];// 否则使用反向图 helse vec[i]= h[i];}// 定义一个小顶堆优先队列 pq,用于存储待处理的节点及其距离
priority_queue<PII, vector<PII>, greater<PII>> pq;// 初始化所有节点的最短距离为一个很大的值for(int i =1; i <= n; i++) d[i]=1061109567;// 点 p 到自身的距离为 0,并将其加入优先队列
pq.push({d[p]=0, p});// 循环处理优先队列中的节点while(pq.size()){// 取出距离最小的节点auto t = pq.top();
pq.pop();// 如果该节点已经被访问过,跳过if(vis[t.y])continue;// 标记该节点为已访问
vis[t.y]=true;// 遍历该节点的所有邻接节点for(auto k : vec[t.y]){// 如果通过当前节点到达邻接节点的距离更短,更新最短距离if(d[k.y]> d[t.y]+ k.x){
d[k.y]= d[t.y]+ k.x;// 将更新后的邻接节点加入优先队列
pq.push({d[k.y], k.y});}}}}intmain(){// 输入节点数量 n、边的数量 m 和指定节点 p
cin >> n >> m >> p;// 循环读取每条边的信息while(m--){int u, v, t;
cin >> u >> v >> t;// 在正向图中添加边
g[u].push_back({t, v});// 在反向图中添加边
h[v].push_back({t, u});}// 调用 Dijkstra 算法计算从点 p 到其他点的最短距离dijkstra(d1,1, vis1);// 调用 Dijkstra 算法计算从其他点到点 p 的最短距离dijkstra(d2,0, vis2);// 用于存储最大的往返最短路径和int maxx =0;// 遍历所有节点,计算最大的往返最短路径和for(int i =1; i <= n; i++){
maxx =max(maxx, d1[i]+ d2[i]);}// 输出最大的往返最短路径和
cout << maxx << endl;return0;}
最小生成树
旅行销售员
问题描述
输入
26712323331534445446365533121232313
输出
42
代码1(kruskal)
#include<bits/stdc++.h>usingnamespace std;// 使用 long long 类型的别名 llusing ll =longlong;// 定义边的结构体,包含边的两个端点 x 和 y,以及边的权重 cstructEdge{
ll x, y, c;// 重载小于运算符,用于边的排序,按照边的权重从小到大排序booloperator<(const Edge &u)const{return c < u.c;}};// 定义常量 N,作为数组的最大长度constint N =1e6+10;// n 表示顶点数,m 表示边数int n, m;// fa 数组用于并查集,存储每个节点的父节点int fa[N];// 并查集的查找操作,用于查找节点 x 所在集合的代表元素// 同时进行路径压缩,将节点 x 直接连接到集合的代表元素上intfind_set(int x){if(x == fa[x])return x;else{
fa[x]=find_set(fa[x]);return fa[x];}}intmain(){int t;// 输入测试用例的数量
cin >> t;while(t--){// 存储所有边的向量
vector<Edge> es;// 输入当前测试用例的顶点数和边数
cin >> n >> m;for(int i =1; i <= m; i++){
ll x, y, c;// 输入每条边的两个端点和权重
cin >> x >> y >> c;// 将边添加到向量中
es.push_back({x, y, c});}// 对边按照权重从小到大进行排序sort(es.begin(), es.end());// 初始化并查集,每个节点的父节点是它自己for(int i =1; i <= n; i++) fa[i]= i;// 用于存储最小生成树中最大边的权重
ll ans =0;// 遍历排序后的边for(auto k : es){// 如果边的两个端点已经在同一个集合中,跳过该边if(find_set(k.x)==find_set(k.y))continue;// 更新最大边的权重
ans =max(ans, k.c);// 合并边的两个端点所在的集合
fa[find_set(k.x)]=find_set(k.y);}// 输出最小生成树中最大边的权重
cout << ans << endl;}return0;}
代码2(prim)
#include<bits/stdc++.h>usingnamespace std;// 使用 long long 类型的别名 llusing ll =longlong;// 定义边的结构体,包含边的终点 u 和边的权重 cstructEdge{
ll u, c;// 重载小于运算符,用于优先队列的比较,使得权重小的边优先级高booloperator<(const Edge &u)const{return c > u.c;}};// 定义常量 N 作为数组的最大长度,inf 作为无穷大的值const ll N =1e6+10, inf =2e18;// n 表示顶点数,m 表示边数int n, m;// g 数组用于存储图的邻接表,每个顶点对应一个存储边的向量
vector<Edge> g[N];// vis 数组用于标记顶点是否已经被访问过bool vis[N];// d 数组用于存储每个顶点到最小生成树的最小距离
ll d[N];// Prim 算法,用于求解最小生成树中最大边的权重
ll prim(){// 初始化所有顶点为未访问状态memset(vis,false,sizeof vis);// 定义优先队列,存储边,按照边的权重从小到大排序
priority_queue<Edge> pq;// 将顶点 1 加入优先队列,初始距离为 0
pq.push({1, d[1]=0});// 用于存储最小生成树中最大边的权重
ll res =0;// 当优先队列不为空时while(pq.size()){// 取出优先队列中权重最小的边的终点int x = pq.top().u;
pq.pop();// 如果该顶点已经被访问过,跳过if(vis[x])continue;// 标记该顶点为已访问
vis[x]=true;// 更新最大边的权重
res =max(res, d[x]);// 遍历该顶点的所有邻接边for(auto k : g[x]){// 邻接边的终点int y = k.u;// 邻接边的权重int w = k.c;// 如果通过当前边到达邻接顶点的距离更小if(w < d[y]){// 更新邻接顶点到最小生成树的最小距离
pq.push({y, d[y]= w});}}}return res;}intmain(){int t;// 输入测试用例的数量
cin >> t;while(t--){// 输入当前测试用例的顶点数和边数
cin >> n >> m;// 清空每个顶点的邻接表,初始化每个顶点到最小生成树的距离为无穷大for(int i =1; i <= n; i++) g[i].clear(), d[i]= inf;for(int i =1; i <= m; i++){
ll x, y, c;// 输入每条边的两个端点和权重
cin >> x >> y >> c;// 无向图,将边添加到两个顶点的邻接表中
g[x].push_back({y, c});
g[y].push_back({x, c});}// 调用 Prim 算法求解最小生成树中最大边的权重并输出
cout <<prim()<< endl;}return0;}
困难图
问题描述
输入
331210136232
输出
14
代码
#include<bits/stdc++.h>usingnamespace std;// 定义长整型别名 llusing ll =longlong;// 定义边的结构体,包含边的两个端点 x、y 以及边的权重 cstructedge{
ll x, y, c;// 重载小于运算符,用于对边按权重从小到大排序booloperator<(const edge &e)const{return c < e.c;}};// 定义常量 N,作为数组的最大长度constint N =2e5+10;// 存储所有边的向量
vector<edge> es;// n 表示顶点数量,m 表示边的数量int n, m;// fa 数组用于并查集,存储每个顶点的父节点// siz 数组用于记录每个连通分量的大小
ll fa[N], siz[N];// 查找 x 所在集合的代表元素,使用路径压缩优化intfind_set(int x){// 如果 x 是自身的父节点,说明 x 是集合的代表元素if(x == fa[x])return x;else{// 递归查找并更新 x 的父节点为集合的代表元素
fa[x]=find_set(fa[x]);return fa[x];}}// 合并 x 和 y 所在的集合,使用按秩合并优化voidmerge(int x,int y){// 找到 x 和 y 所在集合的代表元素int ra =find_set(x);int rb =find_set(y);// 保证将较小的集合合并到较大的集合中if(siz[ra]> siz[rb])swap(ra, rb);// 将 ra 的父节点设为 rb
fa[ra]= rb;// 更新 rb 所在集合的大小
siz[rb]+= siz[ra];}intmain(){// 输入顶点数量 n 和边的数量 m
cin >> n >> m;// 循环读取每条边的信息for(int i =1; i <= m; i++){
ll u, v, w;
cin >> u >> v >> w;// 将边信息添加到边的向量中
es.push_back({u, v, w});}// 初始化并查集,每个顶点的父节点是自身,集合大小为 1for(int i =1; i <= n; i++) fa[i]= i, siz[i]=1;// 用于存储最终结果
ll ans =0;// 对边按权重从小到大排序sort(es.begin(), es.end());// 遍历排序后的边for(auto k : es){// 如果边的两个端点已经在同一个集合中,跳过该边if(find_set(k.x)==find_set(k.y))continue;// 计算该边对结果的贡献,即两个连通分量大小的乘积乘以边的权重
ans += siz[find_set(k.x)]* siz[find_set(k.y)]* k.c;// 合并边的两个端点所在的集合merge(k.x, k.y);}// 输出最终结果
cout << ans;return0;}
矿石建设
问题描述
输入
321133512242
输出
8.49
样例解释
代码
#include<bits/stdc++.h>usingnamespace std;// 定义宏,方便访问 pair 类型的元素#definexxfirst#defineyysecond// 定义结构体 node,用于存储边的信息// x 和 y 表示边的两个端点,w 表示边的权重structnode{int x, y;double w;} g[10005];// 定义两个数组,分别存储村庄和矿石点的坐标// c 数组存储村庄的坐标,k 数组存储矿石点的坐标
pair<double,double> c[104];
pair<double,double> k[104];// 并查集的父节点数组,用于判断两个节点是否属于同一个集合int fa[104];/*
总的思路就是先把村庄的最小权值找出来,在枚举矿石的点到村庄最近的距离
*/// 并查集的查找函数,用于查找节点 x 所在集合的根节点// 同时进行路径压缩,提高后续查找的效率introot(int x){// 如果 x 是根节点,直接返回 xif(x == fa[x])return x;else{// 路径压缩,将 x 的父节点直接设为根节点
fa[x]=root(fa[x]);return fa[x];}}// 比较函数,用于对边按照权重从小到大排序boolcmp(node a, node b){return a.w < b.w;}intmain(){int n, m;// 输入村庄的数量 n 和矿石点的数量 m
cin >> n >> m;// 输入每个村庄的坐标,并存储在 c 数组中for(int i =1; i <= n; i++){double x, y;
cin >> x >> y;
c[i]={x, y};}// 输入每个矿石点的坐标,并存储在 k 数组中for(int i =1; i <= m; i++){double x, y;
cin >> x >> y;
k[i]={x, y};}// 边的计数器,用于记录生成的边的数量int cnt =1;// 枚举村庄之间的所有边,计算边的权重并存储在 g 数组中for(int i =1; i <= n; i++){for(int j = i +1; j <= n; j++){// 计算两点在 x 轴和 y 轴上的距离的平方double lx =pow(c[i].xx - c[j].xx,2);double ly =pow(c[i].yy - c[j].yy,2);// 计算两点之间的欧几里得距离double l =sqrt(lx + ly);// 将边的信息存储在 g 数组中
g[cnt++]={i, j, l};}}// 对边按照权重从小到大排序// 原代码此处有错误,正确的应该是 sort(g + 1, g + cnt, cmp);sort(g +1, g +1+ cnt, cmp);// 初始化并查集,每个节点的父节点初始化为自身for(int i =1; i <= n; i++)
fa[i]= i;// 用于存储最小生成树的总权重double ans =0;// 遍历所有边,使用 Kruskal 算法构建最小生成树for(int i =1; i <= cnt; i++){// 如果边的两个端点已经在同一个集合中,跳过该边if(root(g[i].x)==root(g[i].y))continue;// 将该边的权重累加到最小生成树的总权重中
ans += g[i].w;// 合并边的两个端点所在的集合
fa[root(g[i].x)]=root(g[i].y);}// 枚举每个矿石点,找出其到所有村庄的最短距离for(int i =1; i <= m; i++){// 初始化最短距离为一个很大的值double mi =1e17;for(int j =1; j <= n; j++){// 计算矿石点到村庄在 x 轴和 y 轴上的距离的平方double lx =pow(k[i].xx - c[j].xx,2);double ly =pow(k[i].yy - c[j].yy,2);// 计算矿石点到村庄的欧几里得距离double l =sqrt(lx + ly);// 更新最短距离
mi =min(mi, l);}// 将最短距离累加到总权重中
ans += mi;}// 输出最终的总权重,保留两位小数printf("%.2lf", ans);return0;}
道路加固
输入
56112513423324235145635
输出
154
代码
#include<bits/stdc++.h>usingnamespace std;// 定义边的结构体structedge{// 边的起点int x;// 边的终点int y;// 边的权重int c;// 重载小于运算符,用于对边按权重进行排序booloperator<(const edge &u)const{return c < u.c;}};// 并查集的父节点数组,用于判断两个点是否连通int fa[10005];// 查找元素 x 所在集合的根节点,并进行路径压缩introot(int x){// 如果 x 是根节点,直接返回 xif(x==fa[x])return x;else{// 递归查找根节点,并将 x 的父节点设为根节点
fa[x]=root(fa[x]);return fa[x];}}// 存储所有边的向量
vector<edge>g;// 二维数组,用于标记禁止使用的边bool is[10005][10005];intmain(){// n 表示顶点数量,m 表示边的数量,k 表示禁止使用的边的数量int n,m,k;
cin >> n >> m >> k;// 读取所有边的信息for(int i=1;i<=m;i++){// a 为起点,b 为终点,c 为边的权重int a,b,c;
cin >> a >> b >> c;// 将边添加到向量 g 中
g.push_back({a,b,c});}// 读取禁止使用的边的信息for(int i=1;i<=k;i++){// x 和 y 为禁止边的两个端点int x,y;
cin >> x >> y;// 标记该边为禁止使用
is[x][y]=1;// 无向图,对称标记
is[y][x]=1;}// 对所有边按权重从小到大进行排序sort(g.begin(),g.end());// 初始化并查集,每个顶点的父节点是其自身for(int i=1;i<=n;i++) fa[i]=i;// ans 用于记录最小生成树的总权重int ans=0;// cnt 用于记录最小生成树中边的数量int cnt=0;// 遍历所有边for(auto k:g){// 如果该边是禁止使用的边,或者两个端点已经在同一个集合中,则跳过if(is[k.x][k.y]||root(k.x)==root(k.y))continue;// 若边可用,则将边的权重累加到总权重中
ans+=k.c;// 边的数量加 1
cnt++;// 合并两个端点所在的集合
fa[root(k.x)]=root(k.y);}// 输出最小生成树的总权重
cout << ans << endl;// 输出最小生成树中边的数量
cout << cnt;return0;}