Wpp


私信TA

用户名:AbertW

访问量:4246

签 名:

等  级
排  名 901
经  验 3522
参赛次数 1
文章发表 8
年  龄 0
在职情况 学生
学  校 北京工商大学
专  业

  自我简介:

解题思路:最小生成树问题,可以用Kruskal或Prim算法解决。

Kruskal:

    算法思路:将图的边按升序排序,每次选择当前最小边,若加入该边不会构成回路,则加入。

    参考代码:

/*
 *Kruskal算法实现思路:
 *按边权重升序排序,每次取当前最小权重边(u,v),判断顶点(u,v)的加入是否会
 *形成环路,如果不形成则该边可以作为最小生成树的边,否则跳过
 *这里是否形成环路的判断可以用并查集实现 
 */
#include<cstdio>
#include<algorithm>
using namespace std;

const int Max_N = 50;

struct Edge//边结构体
{
	int u,v,w;
	Edge(){}
	Edge(int u_,int v_,int w_)
	{
		u = u_; v = v_; w = w_; //构造函数 
	}
	bool operator < (const Edge& e) const
	{
		return w<e.w;   
	}
}edge[Max_N*Max_N];  //E<=V^2

int n;
/* 并查集  */
int par[Max_N]; //父亲 
int height[Max_N];  //树的高度(为压缩路径) 
void init(int n); //初始化 
int find( int x ); //x的父亲
void unite( int x, int y ); //合并x,y
bool same( int x, int y); //x,y是否在同一组 
/* 并查集 */ 
void MST_Kruskal(Edge *, int );

int main()
{
	scanf("%d",&n); 
	int index = 0;
	for( int i=0; i<n; i++ )
	{
		for( int j=0; j<n; j++ )
		{
			int w;
			scanf("%d",&w);
			if( i0 ) //i<j 因为是对称矩阵,只需取一半 且i==j时w==0 
			{
				edge[index++] = Edge(i,j,w);
			}
		}
	}
	
	MST_Kruskal(edge,index);
	
	return 0;
}

void init(int n)
{
	for( int i=0; i<n; i++ )
	{
		par[i] = i;
		height[i] = 0;
	}
}

int find( int x )
{
	return x==par[x] ? x : par[x] = find( par[x] );
}

void unite( int x, int y )
{
	x = find( x );	y = find( y );
	if( x==y )
		return;
	if( height[x]<height[y] )
	{
		par[x] = y;
	}
	else
	{
		par[y] = x;
		if( height[x]==height[y] )	height[x]++;
	}
}

bool same( int x , int y )
{
	return find(x)==find(y);
}

void MST_Kruskal(Edge *edge, int length )
{
	/*初始化*/
	init(n);
	sort(edge,edge+length);
	
	int cnt = 0, res = 0;
	for( int i=0; i<length; i++ )
	{
		int u = edge[i].u, v = edge[i].v, w = edge[i].w;
		if( same(u,v) )	continue;  //已经在同一颗树中,如果再加入(u,v)会形成回路
		
		res += w;
		unite(u,v); //加入同一组(可以认为同一个联通分量中) 
		cnt++;
		
		if( cnt==n-1 )	break; //边的数目只需n-1 
	}
	
	printf("%d\n",res);
}


Prim:

    算法思路:与Dijkstra算法类似,保存一个集合作为当前最小生成树的顶点子集A,每次选取连接A与V-A的最小权重的边,并将边的顶点加入集合A中。

并以该顶点更新与之相连的V-A中顶点的权重key值。

    参考代码:

/*
 *最小生成树:Prim算法实现
 *题目仅仅要求最小生成树代价,所以可以简单实现,不用记录树的结构
 *Prim算法思想:最小生成树子集A始终为一颗树,
 *每次在V-A中找安全便加入A中
 *其快速实现是给每个顶点一个权重,表示其到A的最短距离,若无连接则为INF
 *可以用有限队列保存,每次取key最小 
 *这里因暂时不会在C++中实现对队列中某一元素值的快速更新,所以采用
 *全局变量key保存且可能在pqueue中可能会有重复顶点 所以增加一段代码以删除重复结点 
 */
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;

const int INF   = 1e8;
const int Max_N = 50; 

struct Vertex//顶点 
{
	int v;
	int key;
	Vertex( int v_, int key_ ){v = v_; key = key_;}
	bool operator<(const Vertex &ver) const
	{
	    return key>ver.key;
	}
};

struct Edge//边 
{
	int to;
	int w;
	Edge(int to_, int w_ ){to = to_; w = w_;}
};

int n;
int key[Max_N];       //每个顶点的权重  
bool visited[Max_N];  //快速判断顶点是否已经在集合A中 
vector<Edge> graph[Max_N];  //带权邻接图(适合稀疏图)

void MST_Prim( int r , int n )
{
	/*初始化*/ 
	for( int i=0; i<n; i++ )
	{
		key[i]	   = INF;
		visited[i] = false;
	}
	
	key[r] = 0;
	priority_queue<Vertex> pque;  //A中顶点 
	Vertex temp_v = Vertex(r,0);
	pque.push( temp_v );
	
	while( pque.size() )
	{
		while( pque.size() && visited[pque.top().v] )
		{
			pque.pop(); //可能出现重复顶点如队列情况 
		}
		if( pque.empty() )	break;
		
		Vertex cur_v = pque.top(); pque.pop();
		int v = cur_v.v;
		visited[v] = true;
		
		for( int i=0; i<graph[v].size(); i++ )
		{
			int u = graph[v][i].to;
			int key_ = graph[v][i].w;
			if( !visited[u] && key_<key[u] )
			{
				key[u] = key_;
				pque.push( Vertex(u,key_) );
			}
		}
	}
}

void solve()
{
	MST_Prim(0,n);
	int res = 0;
	for( int i=0; i<n; i++ )	res += key[i];
	printf("%d\n",res);
}

int main()
{
	scanf("%d",&n);
	for( int i=0; i<n; i++ )
	{
		for( int j=0; j<n; j++ )
		{
			int w;
			scanf("%d",&w);
			if( w>0 )	graph[i].push_back( Edge(j,w) );
		}
	}
	
	solve();
	
	return 0;
}


 

0.0分

1 人评分

  评论区

  • «
  • »