一、最小生成树
说到生成树首先要解释一下树,树是一个联通的无向无环图,多棵树的集合则被称为森林。
因此,树具有许多性质:
1.两点之间的路径是唯一的。
2.边数等于点数减一。
3.连接任意两点都会生成一个环。
对于一个无向联通图G的子图,如果它包含G的所有点,则它被称为G的生成树,而各边权和最小的生成树则被称为最小权重生成树,简称最小生成树。
对于最小生成树问题有两种算法,一种是kruskal算法,另一种是prime算法。
kruskal算法:
kruskal算法就是在得到一张图之后把图上所有边按边权从小到大排序,然后删掉所有的边,再按排完的顺序把边一条一条地加进去,加边的同时用并查集维护两点间的连通性,如果一条边的两个端点已经联通,则不将这条边加入到图中,直到加了n-1条边为止。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n,x,y;
double l;
double s;
int f[100001];
int i=0;
int sum;
double total=0;
struct demacia
{
int a;
int b;
double d;
};
demacia m[100001];
int cmp(demacia x,demacia y)
{
return x.d<y.d;
}
int find(int x)
{
if(f[x]==x)
{
return x;
}
else
{
f[x]=find(f[x]);
return f[x];
}
}
int main()
{
scanf("%lf",&s);
scanf("%d",&n);
while(scanf("%d%d%lf",&x,&y,&l) != EOF)
{
m[i].a=x;
m[i].b=y;
m[i].d=l;
i++;
}
sum=i;
sort(m,m+sum,cmp);
for(int k=1;k<=n;k++)
{
f[k]=k;
}
for(int j=0;j<sum;j++)
{
int fx=find(m[j].a);
int fy=find(m[j].b);
if(fx!=fy)
{
total=total+m[j].d;
f[fy]=fx;
}
}
if(total>s||sum<n-1)
{
printf("Impossible");
}
else
{
printf("Need %.2lf miles of cable",total);
}
}
prime算法:
prime算法实际上就是一个贪心;把图中的点分成未处理的和处理过的两部分,每一次找到联通处理过的和未处理的两部分的所有边,连接边权最小的一条,用并查集维护即可。
kruskal算法适用于稀疏图而prime算法适用于稠密图。
二、次小生成树
次小生成树分为非严格次小生成树和严格次小生成树;非严格次小生成树是可以和最小生成树权值和相等的,而严格次小生成树是严格大于最小生成树的。
非严格次小生成树是在最小生成树kruskal算法的基础上加上lca(最好用倍增lca),在kruskal之后将未被选取的边依此枚举对每条边的两个端点做lca找出路径上边权最大的边然后将它换掉,直到把所有未选取的边都枚举一遍就求出次小生成树了。
而lca要与处理出来g[i][j]表示以i为起点往上2^j步的祖先与i 之间最大的一条边的边权。
严格次小生成树就只需要再预处理出来次大边就行了,但它要考虑的情况就比较多了。
非严格次小生成树
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m;
int next[100050];
int to[100050];
int val[100050];
int head[10001];
int f[10001][15];
int g[10001][15];
int d[10001];
int F[10001];
int tot=0;
int sum;
int num;
struct node
{
int x;
int y;
int v;
}a[50001];
void add(int x,int y,int v)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=v;
}
int cmp(node a,node b)
{
return a.v>b.v;
}
int find(int x)
{
if(F[x]==x)
{
return x;
}
return F[x]=find(F[x]);
}
void dfs(int x,int fx)
{
f[x][0]=fx;
d[x]=d[fx]+1;
for(int j=1;(1<<j)<=n;j++)
{
f[x][j]=f[f[x][j-1]][j-1];
g[x][j]=min(g[f[x][j-1]][j-1],g[x][j-1]);
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fx)
{
g[to[i]][0]=val[i];
dfs(to[i],x);
}
}
}
int lca(int x,int y)
{
int total=0;
if(d[x]<d[y])
{
swap(x,y);
}
int deep=d[x]-d[y];
for(int i=0;i<=14;i++)
{
if((deep&(1<<i))!=0)
{
total=max(total,g[x][i]);
x=f[x][i];
}
}
if(x==y)
{
return total;
}
for(int j=14;j>=0;j--)
{
if(f[x][j]!=f[y][j])
{
total=max(total,max(g[x][j],g[y][j]));
x=f[x][j];
y=f[y][j];
}
}
return total=max(max(g[x][0],g[y][0]),total);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
F[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
}
sort(a+1,a+1+m,cmp);
num=0;
sum=0;
for(int j=1;j<=m;j++)
{
int fx=find(a[j].x);
int fy=find(a[j].y);
if(fx!=fy)
{
F[fx]=fy;
num++;
sum+=a[j].v;
add(a[j].x,a[j].y,a[j].v);
add(a[j].y,a[j].x,a[j].v);
}
}
g[1][0]=0;
dfs(1,1);
int q;
scanf("%d",&q);
int A,B;
for(int i=1;i<=q;i++)
{
scanf("%d%d",&A,&B);
printf("%d\n",lca(A,B));
}
}
严格次小生成树
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m;
int next[600001];
int to[600001];
int val[600001];
int head[100001];
int f[100001][20];
int g[100001][20];
int g2[100001][20];
int d[100001];
int F[100001];
int tot=0;
long long sum;
long long num;
long long cnt;
long long ans=1000000000000000ll;
struct node
{
int x;
int y;
int v;
}a[300001];
struct node1
{
int x;
int y;
int v;
}b[300001];
void add(int x,int y,int v)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=v;
}
int cmp(node a,node b)
{
return a.v<b.v;
}
int find(int x)
{
if(F[x]==x)
{
return x;
}
return F[x]=find(F[x]);
}
void dfs(int x,int fx)
{
f[x][0]=fx;
d[x]=d[fx]+1;
for(int j=1;j<=19;j++)
{
f[x][j]=f[f[x][j-1]][j-1];
if(g[x][j-1]>g[f[x][j-1]][j-1])
{
g[x][j]=g[x][j-1];
g2[x][j]=max(g[f[x][j-1]][j-1],g2[x][j-1]);
}
else if(g[x][j-1]<g[f[x][j-1]][j-1])
{
g[x][j]=g[f[x][j-1]][j-1];
g2[x][j]=max(g[x][j-1],g2[f[x][j-1]][j-1]);
}
else
{
g[x][j]=g[f[x][j-1]][j-1];
g2[x][j]=max(g2[x][j-1],g2[f[x][j-1]][j-1]);
}
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fx)
{
g[to[i]][0]=val[i];
dfs(to[i],x);
}
}
}
int lca(int x,int y)
{
int total=0;
if(d[x]<d[y])
{
swap(x,y);
}
int deep=d[x]-d[y];
for(int i=0;i<=19;i++)
{
if((deep&(1<<i))!=0)
{
total=max(total,g[x][i]);
x=f[x][i];
}
}
if(x==y)
{
return total;
}
for(int j=19;j>=0;j--)
{
if(f[x][j]!=f[y][j])
{
total=max(total,max(g[x][j],g[y][j]));
x=f[x][j];
y=f[y][j];
}
}
return total=max(max(g[x][0],g[y][0]),total);
}
int lca2(int x,int y,int z)
{
int total=0;
if(d[x]<d[y])
{
swap(x,y);
}
int deep=d[x]-d[y];
for(int i=0;i<=19;i++)
{
if((deep&(1<<i))!=0)
{
if(g[x][i]!=z)
{
total=max(g[x][i],total);
}
else
{
total=max(g2[x][i],total);
}
x=f[x][i];
}
}
if(x==y)
{
return total;
}
for(int j=19;j>=0;j--)
{
if(f[x][j]!=f[y][j])
{
if(g[x][j]!=z)
{
total=max(total,g[x][j]);
}
else
{
total=max(total,g2[x][j]);
}
if(g[y][j]!=z)
{
total=max(g[y][j],total);
}
else
{
total=max(g2[y][j],total);
}
x=f[x][j];
y=f[y][j];
}
}
if(g[x][0]!=z)
{
total=max(total,g[x][0]);
}
else
{
total=max(total,g2[x][0]);
}
if(g[y][0]!=z)
{
total=max(total,g[y][0]);
}
else
{
total=max(total,g2[y][0]);
}
return total;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
F[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
}
sort(a+1,a+1+m,cmp);
num=0;
sum=0;
cnt=0;
int j=1;
for(int j=1;j<=m;j++)
{
int fx=find(a[j].x);
int fy=find(a[j].y);
if(fx!=fy)
{
F[fx]=fy;
num++;
sum+=a[j].v;
add(a[j].x,a[j].y,a[j].v);
add(a[j].y,a[j].x,a[j].v);
}
else
{
cnt++;
b[cnt].x=a[j].x;
b[cnt].y=a[j].y;
b[cnt].v=a[j].v;
}
}
g[1][0]=0;
dfs(1,1);
for(int i=1;i<=cnt;i++)
{
int z=lca(b[i].x,b[i].y);
int k=b[i].v-z;
if(k==0)
{
k=b[i].v-lca2(b[i].x,b[i].y,z);
}
if(ans>k)
{
ans=k;
}
}
long long gg=sum+ans;
printf("%lld",gg);
}