标签:add for 进入 mes 比较 struct std space 题解
这题真的是非常标准的模板题啊
看到连最少的边,第一时间会想到 $kruskal$ .这道题的难点其实就一个:你要注意到连边权最大的边使整个图联通
为什么:题意是第i个点想走到 $pos[i]$ ,也就是说点i和点 $pos[i]$ 必须要联通.
为什么想不到 $kruskal$ :因为 $kruskal$ 是最小生成树,比较难想到两条边权大的边连起来并不会影响最小权值.
扯远了进入正题:
1.怎么保证连的边的权值最大:
将边权从大到小排列,需要拿的边尽量拿(因为并不会影响最小权值).
2.怎么查两点是否连通:
这个是 $kruskal$ 算法最基本的要领.如果一个点的父亲 $fat[i]$ 等于另外一个点的父亲 $fat[j]$, 那么我们可以确定这两点连通
3.要连什么点
细心观察会发现,第i个点走到 $pos[i]$ 可以转化为点i和点 $pos[i]$ 连通.
4.怎么判断什么时候全图连通:
因为要求是一定要全部连通才行,所以如果有一个点不连通,后面怎么连通都没用.而如果有一个点连通了,就算后面再怎么连,最后这个点仍然连通.所以,我们可以做一个指针.指针从1开始,表示第一个不连通的点.每次连边之前先确定还有哪条边不连通.如果我们发现连通的点数已经大于总的点数,那么答案就是输出了
上代码:(124ms吊打目前所有解法)
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int x=0,w=1;
char ch;
while(ch<'0'||ch>'9')
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*w;
}
int n,m,pos[100005],tot = 0,fat[100005], ans = -1;
struct Edge{
int from,to,dis;
}edge[300005];
int father(int x){
while(x!=fat[x]) x=fat[x]=fat[fat[x]];//这个是kruskal的一个加速方法,在查自己的时候可以直接更新父亲
return x;
}
void Union(int x, int y){
fat[father(y)] = father(x);
}
void add(int f, int t, int d){
edge[++tot].from = f;
edge[tot].to = t;
edge[tot].dis = d;
}
//不解释
bool sorted(Edge a, Edge b){
return a.dis>b.dis;//从大到小排列
}
int main(){
n = read();m = read();
for (int i=1;i<=n;i++) pos[i] = read();
for (int i=0;i<m;i++){
int a,b,c;
a = read();b = read(); c = read();
add(a,b,c);//连边
}
sort(edge+1,edge+tot+1,sorted);//从大到小排列
int ptr = 1;
for (int i=1;i<=n;i++) fat[i] = i;//自己爸爸一开始就是自己
for (int i=1;i<=tot;i++){
while(father(ptr)==father(pos[ptr])) {ptr++; if (ptr>n) goto abcd;}//当我爸跟他爸是同一个人就增加,直到我跟他都不存在或者我爸不是他爸
if (father(pos[edge[i].from])!=father(pos[edge[i].to])){
Union(pos[edge[i].from],pos[edge[i].to]);//如果他爸不是我爸,那我将我爸设成他爸
ans = edge[i].dis;//答案就是现在的边权
}
}
abcd:;
printf("%d",ans);
}
题解 P6004 【[USACO20JAN]Wormhole Sort S】
标签:add for 进入 mes 比较 struct std space 题解
原文地址:https://www.cnblogs.com/DannyXu/p/12237529.html