今天简单的探讨了最小生成树和次小生成树,作为树的衍生,最小生成树在生活中有极为广泛的应用,例如最节约布线等等,而次小生成树则不那么应用广泛,但能起到检验最小生成树是否唯一。由此,可以知道次小生成树该如何实现,即替换掉最小生成树中的一些边,使得权值为次小。
最小生成树
Prim算法
设G=(V,E)是一个有向连通图
U是顶点集V的一个子集
如果:
1、边(u,v) , u∈U且v∈(V-U),一个顶点在U中、另一个端点不在U中
2、(u,v)是满足条件1最小权值的边
那么
一定存在G的一棵最小生成树包含(u,v)
任意选取一个起始点(对结果无影响,最小生成树是要经过每一个点的),将他添加到u数组,接下里寻找u数组中所有顶点相连边最小的一条,这条边另一端就是当前步骤的求解(当然这个点没有被处理过,所以用一个visit[]数组表示当前点到底有没有加入u数组)
代码
memset(minn,127,sizeof(minn);
for(i=1;i<=n;i++) minn[i]=map[1][i];
minn[1]=0;vis[1]=1;
for(i=1;i<=n-1;i++)
{
min=INT_MAX;
t=0;
for(j=2;j<=n;j++)
{
if(!vis[j] && minn[j]<min)
{
t=j;
min=minn[j];
}
}
vis[t]=1;
sum+=min;
for(j=2;j<=n;j++)
{
if((!vis[j] && minn[j]>map[t][j])
{
minn[j]=map[t][j];
}
}
}
Kruskal算法
1.初始G(V, {}),即包含所有节点,边为空
2.将原图中所有的边按权值从小到大排序
3.从权值最小的边开始,如果这条边连接的两个节点于图G中不在同一个连通分量中,则添加这条边到图G中
4.重复3,直至图G中所有的节点都在同一个连通分量中
(可以使用并查集判断两个点在同一个连通分量)
算法描述
1.初始化并查集。father[x]=x。
2.tot=0
3.将所有边用快排从小到大排序。
4.计数器 k=0;
5.for (i=1; i<=M; i++) //循环所有已从小到大排序的边
if 这是一条u,v不属于同一集合的边(u,v)(因为已经排序,所以必为最小),
{
①合并u,v所在的集合,相当于把边(u,v)加入最小生成树。
②tot=tot+W(u,v)
③k++
④如果k=n-1,说明最小生成树已经生成,则break;
}
6. 结束,tot即为最小生成树的总权值之和。
例题
最优布线问题
描述
学校需要将n台计算机连接起来,不同的2台计算机之间的连接费用可能是不同的。为了节省费用,我们考虑采用间接数据传输结束,就是一台计算机可以间接地通过其他计算机实现和另外一台计算机连接。
为了使得任意两台计算机之间都是连通的(不管是直接还是间接的),需要在若干台计算机之间用网线直接连接,现在想使得总的连接费用最省,让你编程计算这个最小的费用。
输入
输入第一行为两个整数n(2<=n<=100),表示计算机总数.
此后n行,每行n个整数。第x+1行y列的整数表示连接第x太计算机和第y台计算机的费用
输出
输出只有一行一个整数,表示最省的总连接费用。
样例输入
3
0 1 2
1 0 1
2 1 0
样例输出
2
这道题很明显,给的输入数据都适用于邻接表,自然用prim算法。
代码如下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Point{
int x,y,value;
bool operator<(const Point &aa)const{
return value<aa.value;
}
}point[20000];
int f[3000],sum=0;
int Find_f(int x){
if (f[x]!=x) f[x]=Find_f(f[x]);
return f[x];
}
int check(int x,int y){
return Find_f(x)!=Find_f(y);
}
int main(){
freopen("liaison.in","r",stdin);
freopen("liaison.out","w",stdout);
int n,m,ans=0;
scanf("%d%d",&n,&m);
for (int i=1; i<=n; ++i) f[i]=i;
for (int i=0; i<m; ++i){
int p,x,y,val;
scanf("%d%d%d%d",&p,&x,&y,&val);
if (p==1){
ans+=val; f[Find_f(x)]=Find_f(y);
}
else {point[sum].x=x; point[sum].y=y; point[sum++].value=val;}
}
sort(point,point+sum);
for (int i=0; i<sum; ++i)
if (check(point[i].x,point[i].y)){
f[Find_f(point[i].x)]=Find_f(point[i].y);
ans+=point[i].value;
}
cout<<ans;
fclose(stdin); fclose(stdout);
}