最小生成树

关于prim算法

先把有的点放于一个集合(或者数组)里,这个集合里存放的是所有走过的点。初始值为0或者false表示还没有点

声明一个一维数组用于记录各点的权值[可理解为起始点到目标点的距离],

声明一个二维数组用于记录某点到某一点的权值,如果这两点不可达到,则设置为无穷大

具体执行过程:

先从某一点开始,把这一个开始的点放于声明的一个数组或者集合里,表明这一点已经被访问过。然后再从余下的n-1个点里去找那个权值最小的点并记录该点的位置然后再加上这一点的权值,同时将该点放于集合里表明该点已初访问。再更新权值

再看下图,从下图分析:

1、

先选取一个点作起始点,然后选择它邻近的权值最小的点(如果有多个与其相连的相同最小权值的点,随便选取一个)。如1作为起点。

isvisited[1]=1;   //表明把1加进来说明是已经访问过

pos=1; //记录该位置

//用dist[]数组不断刷新最小权值,dist[i](0<i<=点数)的值为:i点到邻近点(未被标记)的最小距离。

dist[1]=0;  //起始点i到邻近点的最小距离为0

dist[2]=map[pos][2]=4;

dist[3]=map[pos][3]=2;

dist[4]==map[pos][4]=3;

dist[5]=map[pos][5]=MaxInt;  //无法直达

dist[6]=map[pos][6]=MaxInt;

 

2、

再在伸延的点找与它邻近的两者权值最小的点。

//dist[]以3作当前位置进行更新

isvisited[3]=1;

pos=3;

dist[1]=0;   //已标记,不更新

dist[2]=map[pos][2]=4;  //比5小,不更新

dist[3]=2;  //已标记,不更新

dist[4]=map[pos][4]=3;   //比1大,更新

dist[5]=map[pos][5]=MaxInt;

dist[6]=map[pos][6]=MaxInt;

 

3、最后的结果:

百度文库里的图解:

 http://wenku.baidu.com/view/0d15428783d049649b665802.html

当所有点都连同后,结果最生成树如上图所示。

所有权值相加就是最小生成树,其值为2+1+2+4+3=12。

//prim算法

int prim(int n){
    int i,j,min,pos;
    int sum=0;
    memset(isvisited,false,sizeof(isvisited)); //初始化
for(i=1;i<=n;i++)
{
    dist[i]=map[1][i];

}//从1开始
isvisited[1]=true;
dist[1]=MAX;

 //找到权值最小点并记录下位置
    for(i=1;i<n;i++){
        min=MAX;
        //pos=-1;
        for(j=1;j<=n;j++){
            if(!isvisited[j] && dist[j]<min){
                min=dist[j];
                pos=j;
                }
        }
        sum+=dist[pos];//加上权值
        isvisited[pos]=true;
        //更新权值
        for(j=1;j<=n;j++){
            if(!isvisited[j] && dist[j]>map[pos][j]){
                dist[j]=map[pos][j];
                }
            }
        }        
        return sum;

}

 



http://acm.hdu.edu.cn/showproblem.php?pid=1879列题

Kruskal算法:

#include<stdio.h>
#include<algorithm>
#define N 110*110/2
int m,n,u[N],v[N],w[N],p[N],r[N];
int cmp( int i, int j)
{
    return w[i]<w[j];
}//间接排序函数
int find(int x)
{
    return p[x]==x?x:find(p[x]);
}
int bbt()
{
    int x,y,i,ans=0;
    for(i=0;i<=n;i++) p[i]=i;//初始化并查集;
    for(i=0;i<m;i++) r[i]=i;//初始化边序号;
    std::sort(r,r+m,cmp);//给边排序;按照权值的大小排序
	for(i=0;i<m;i++)
    {
        int e=r[i];
        x=find(u[e]);//找出当前边两个端点所在集合;
        y=find(v[e]);
        if(x!=y) { ans+=w[e]; p[x]=y;}//如果在不同集合,合并
    }
    return ans;
}
int main()
{
    int i,ans,cc;
	while(~scanf("%d",&n))
    {
        if(n==0) break;
        m=n*(n-1)/2;
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d%d",&u[i],&v[i],&w[i],&cc);
            if(cc==1) w[i]=0;
        }
		ans=bbt();
         printf("%d\n",ans);
    }
    return 0;
}
prim算法:

//HDOJ 1879 AC
#include <iostream>
#include <cmath>

using namespace std;
#define MAX 99999
#define LEN 101
int dist[LEN];
int map[LEN][LEN];
bool isvisited[LEN];

//初始化
void init(){
	int i,j;
	for(i=0;i<LEN;i++){
		for(j=0;j<LEN;j++){
			if(i==j) map[i][j]=0;   //对a[][]进行初始化,一般都要;
			map[i][j]=MAX;
		}
	}
}


//prim算法
int prim(int n){
	int i,j,min,pos;
	int sum=0;
	memset(isvisited,false,sizeof(isvisited));

	//初始化
	for(i=1;i<=n;i++){
		dist[i]=map[1][i];
	}

	//从1开始
	isvisited[1]=true;
	dist[1]=MAX;

	//找到权值最小点并记录下位置
	for(i=1;i<n;i++){
		min=MAX;
		//pos=-1;
		for(j=1;j<=n;j++){
			if(!isvisited[j] && dist[j]<min){
				min=dist[j];
				pos=j;
			}
		}	
		sum+=dist[pos];//加上权值
		isvisited[pos]=true;

		//更新权值
		for(j=1;j<=n;j++){
			if(!isvisited[j] && dist[j]>map[pos][j]){
				dist[j]=map[pos][j];
			}
		}
	}	
	return sum;
}

int main(){

	int n;

	while(cin>>n){
		init();
		if(n==0) break;

		int i,j,a,b,d,state;

		for(i=0;i<n*(n-1)/2;i++){

			cin>>a>>b>>d>>state;

			if(state==1){
				map[a][b]=map[b][a]=0;
			}
			else{
				map[a][b]=map[b][a]=d;
			}
		}
		cout<<prim(n)<<endl;

	}	
	 return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值