最小生成树(Minimal Spanning Tree,MST)
包含图中n个点和边权和最小的n-1条边 的一棵树
Kruskal------ O ( m l o g 2 m ) O(mlog_2m) O(mlog2m)
默认n点数m边数
- 对从短到长把边 ( u , v ) (u,v) (u,v)放入树中
- 并查集判圈, u u u作为 v v v的父亲
如果两点已经在一个并查集中,说明这条边会和之前放入的边形成圈,不放入
模板
cpp
const int N = 5e3+10,M=1050;
const double PI=acos(-1);
const long long mod =998244353, inf = 1e18 ;
struct edge
{
int u,v,w;
};
int n,m;
int fa[N];
vector<edge>e;
int find(int x){
return fa[x]=(fa[x]==x?x:find(fa[x]));
}
void kruskal(){
forr(i,1,n)fa[i]=i;
sort(e.begin(),e.end(),[&](edge x,edge y){
return x.w<y.w;
});
int ans=0,cnt=0;// 长度和 边数<=n-1
for(auto [u,v,w]:e){
if(cnt==n-1)break;
int fu=find(u),fv=find(v);
if(fu==fv)continue;
else {// 放入边
fa[fv]=fu;
ans+=w;
cnt++;
}
}
if(cnt==n-1)cout<<ans<<endl;// 能联通所有点
else cout<<"orz"<<endl;//不连通
}
void solve(){
cin>>n>>m;
forr(i,1,m){
int x,y,z;
cin>>x>>y>>z;
e.push_back({x,y,z});
}
kruskal();
}
Prim算法------ O ( m l o g 2 n ) O(mlog_2n) O(mlog2n)
类似Dijkstra 维护一个已加入树的点集 U U U 每次向外拓展一个离点集最近的点
模板
cpp
const int N = 5e3+10,M=1050;
const double PI=acos(-1);
const long long mod =998244353, inf = 1e18 ;
int n,m;
vector<pii>e[N];
void prim(){
int ans=0,cnt=0;
vector<int>U(n+1,0);
int bg=1;
priority_queue<pii>q;
q.push({0,bg});
while (q.size())
{
auto [dis,now]=q.top();q.pop();
if(U[now])continue;
U[now]=1;
ans+=-dis;
cnt++;
for(auto [x,w]:e[now]){
if(U[x])continue;
q.push({-w,x});
}
}
if(cnt==n)cout<<ans<<endl;
else cout<<"orz"<<endl;
}
void solve(){
cin>>n>>m;
forr(i,1,m){
int x,y,z;
cin>>x>>y>>z;
e[x].push_back({y,z});
e[y].push_back({x,z});
}
prim();
}
例题
蓝桥杯省A 吊坠 最大生成树+环变链 线性dp找最长公共子串
cpp
const int N = 205,M=1050;
const double PI=acos(-1);
const long long mod =998244353, inf = 1e18 ;
int n,m;
string s[N];
int find_w(int x,int y){
vector<vint>dp(2*m+1,vint(2*m+1,0));
int res=0;
forr(i,1,2*m){
forr(j,1,2*m){
if(s[x][i-1]==s[y][j-1]){
dp[i][j]=min(dp[i-1][j-1]+1,m);
res=max(dp[i][j],res);
}// 注意是子串 子串是连续的
}
}
return res;
}
struct edge
{
int u,v,w;
};
vector<edge>e;
int fa[N];
int find(int x){
return fa[x]=(fa[x]==x?x:find(fa[x]));
}
void kruskal(){
forr(i,1,n)fa[i]=i;
sort(e.begin(),e.end(),[&](edge x,edge y){
return x.w>y.w;
});
int ans=0,cnt=0;
for(auto [u,v,w]:e){
if(cnt==n-1)break;
int fu=find(u),fv=find(v);
if(fu==fv)continue;
fa[fv]=fu;
cnt++;
ans+=w;
}
cout<<ans<<endl;
}
void solve(){
cin>>n>>m;
forr(i,1,n){
cin>>s[i];
s[i]+=s[i];
}
forr(i,1,n){
forr(j,i+1,n){
int w=find_w(i,j);
e.push_back({i,j,w});
}
}
kruskal();
}