图论算法总结之六:最小生成树

六、最小生成树

生成树:由一个图的所有点与部分边构成的连通又无回路的树

最小生成树:权值最小的生成树

1.Prim算法

(1)思想:

设置点集T,边集U,一开始T,U为空,首先任意选取一点V0加入T,更新所有以V0为端点的边的另一端点Vi到T中点的最小距离dis[i],然后进行以下操作:

①选取不在T中的点vj,vj到T的距离是可选点中最小的,将vj加入T

②将连接vj的边,而且另一端点不在T中的边加入U

③更新所有加入边的另一端点vk的dis[k],dis[k]=min{dis[k],val(j,k)}

重复这三步,直到|T|=n

注意:②中所加入的边的另一端点一定不再T中,否则就会成环

(2)分析:

遍历取最小边o(v^2),堆取最小边o(Elog(v))

不加堆优化的Prim适用于密集图

加堆优化的Prim适用于稀疏图

(3)模板:

POJ - 1258

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define INF 1<<30;
int n;
struct edge{
	int to,cost;
	edge(int to,int cost):to(to),cost(cost){}
	bool operator < (const edge& e)const{
		return cost>e.cost;
	}
};
vector<edge> G[110];
int prim(void){
	priority_queue<edge> q;
	int num=0;
	int ans=0;
	int dis[110];
	for(int i=1;i<=n;i++){
		dis[i]=INF;
	}
	int vis[110];
	memset(vis,0,sizeof(vis));
	edge e(1,0);
	edge te(0,0);
	q.push(e);
	while(num<n&&!q.empty()){
		do{
			te=q.top();q.pop();
			//cout<<te.to<<' '<<te.cost<<endl; 
		}while(vis[te.to]==1&&!q.empty());
		if(vis[te.to]==0){
			num++;ans+=te.cost;vis[te.to]=1;
			for(int i=0;i<G[te.to].size();i++){
				int k=G[te.to][i].to,c=G[te.to][i].cost;
				if(vis[k]==0){
					if(dis[k]>c){
						dis[k]=c;
						q.push(edge(k,c));
					}
				}
			}
		}
	}
	if(num<n)return -1;
	return  ans;
}
int main(){
	while(cin>>n){
		for(int i=1;i<=n;i++){
			G[i].clear();
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				int c;cin>>c;
				edge e(j,c);
				G[i].push_back(e);
			}
		}
		cout<<prim()<<endl;
	}
	return 0;	
} 

2.Kruskal算法

(1)思想:

将所有边从小到大排序,选取最短的边,且边的两个端点分属不同的集合,一直加满n-1条边为止

(2)分析:

边排序最耗时,为O(ElogE)

(3)模板代码:

POJ - 1258

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>

#define MEM(a,x) memset(a,x,sizeof(a))
#define ll long long  
#define INF 0x3f3f3f3f  
#define EXP 1e-10  
 
using namespace std;
int n;
struct edge{
	int s,e,v;
	edge(int s,int e,int v):s(s),e(e),v(v){}
	bool operator < (const edge& e)const{
		return v<e.v;
	}
};
vector<edge> G;
int fa[110];
int find(int i){
	if(fa[i]==i)return i;
	return fa[i]=find(fa[i]);
}
void unite(int i,int j){
	int fi=find(i);
	int fj=find(j);
	if(fi==fj)return;
	fa[fi]=fj;
} 
int main(){
	while(cin>>n){
		G.clear();
		for(int i=1;i<=n;i++){
			fa[i]=i;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				int val;
				cin>>val;
				G.push_back(edge(i,j,val));
			}
		}
		sort(G.begin(),G.end());
		int ans=0,num=0;
		for(int i=1;num<n-1&&i<G.size();i++){
			int ss=G[i].s,ee=G[i].e,vv=G[i].v;
			if(find(ss)!=find(ee)){
				ans+=vv;
				num++;
				unite(ss,ee);
			}
		}
		if(num<n-1)cout<<"-1"<<endl;
		else cout<<ans<<endl;
	}
	return 0;
}

3.补充应用

POJ - 2349

分析:n个村庄,k个卫星,问剩余村庄最小的最大连接距离d

不考虑卫星的情况下,d要尽可能小,如果我们求出了最小生成树,那么这个生成树上最长的边就是要求的d;当我们考虑卫星的时候,实际上就是说可以有多棵子树,子树之间用卫星联系,求子树的d即可;实际操作就是将前k-1大的边删去,第k大的边就是答案

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#include<iomanip>

#define MEM(a,x) memset(a,x,sizeof(a))
#define ll long long  
#define INF 0x3f3f3f3f  
#define EXP 1e-10  
 
using namespace std;
int k,n;
double x[510],y[510];
struct edge{
	int s,e;
	double v;
	edge(int s,int e,double v):s(s),e(e),v(v){}
	bool operator < (const edge& e)const{
		return v<e.v;
	}
};
vector<edge> G;
vector<double> EDGE;
int fa[510];
int find(int i){
	if(fa[i]==i)return i;
	return fa[i]=find(fa[i]);
}
void unite(int i,int j){
	int fi=find(i);
	int fj=find(j);
	if(fi==fj)return;
	fa[fi]=fj;
}
int main(){
	int m;
	cin>>m;
	while(m--){
		G.clear();
		EDGE.clear(); 
		cin>>k>>n;
		for(int i=1;i<=n;i++){
			fa[i]=i;
		}
		for(int i=1;i<=n;i++){
			cin>>x[i]>>y[i];
		}
		//cout<<x[3]<<' '<<y[3]<<endl;
		//cout<<x[4]<<' '<<y[4]<<endl;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				double val;
				if(i==j)val=0;
				else{
					val=sqrt(abs((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])));
				}
				G.push_back(edge(i,j,val));
			}
		}
		sort(G.begin(),G.end());
		int num=0;
		for(int i=0;num<n-1&&i<G.size();i++){
			int ss=G[i].s,ee=G[i].e;
			double vv=G[i].v;
			if(find(ss)!=find(ee)){
				EDGE.push_back(vv);
				unite(ss,ee);
				num++;
			}
		}
		sort(EDGE.begin(),EDGE.end());
		cout<<fixed<<setprecision(2)<<EDGE[n-k-1]<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41333528/article/details/80390968
今日推荐