prim——最小连接路径和(hdu1301)

本文详细介绍使用Prim算法解决最小生成树问题的过程。通过具体示例讲解如何不断更新顶点集合,选择最优边来构建最小生成树。

*本题的Kruskal解法http://blog.csdn.net/sm9sun/article/details/53257264

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1301

题目描述:

给定村庄数n,用字母表的前n个字母表示,接下来n-1行每行一个村庄字母和与其连接的村庄数以及各村庄的字母和距离。求最小生成树。

解题思路:

先说明一下prim算法:

普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小


我们假设5个点A,B,C,D,E  其互相的关系(边的权值)为以下矩阵(二维数组),然后简单写几个边

Side(A,B)=Side(B,A)=1                  //AB的边权值 为1   prim适用于无向图,所以记得双向赋值

Side(A,C)=Side(C,A)=2   

Side(A,E)=Side(E,A)=5 

Side(B,E)=Side(B,A)=3

Side(C,D)=Side(D,C)=4


我们填充一下数组,因为求最小的生成树,所以本身(A,A)我们设为0,没有连接的我们设为M(MAX)

 ABCDE
A012M5
B10MM3
C2M04M
DMM40M
E53MM0

我们看一下如何连通这些点,首先以第一个为起点(反正所有的点我们都要连接)

与A最近的点即B,我们可以这样想,如果其他的点都连接起来了,那么肯定也是以AB边去连接A,所以AB边必不可少。

当AB连接了之后,我们将B与其他点的状态与A共享,也就是,原来A连接不到的(或权值较大的),可以通过B作为桥梁去连接。

我们刷新一下该数组

 ABCDE
A012M3
B10MM3
C2M04M
DMM40M
E33MM0

我们看到,AE的边 发生了变化,因为原有的AE=5权值大于AB,所以当我们选择连接E的时候,我们更倾向于通过B连接,也就等同于A连接。

或者我们可以把A看作(A,B)集合。然后我们再找与其边最小的点(C)

 ABCDE
A01243
B10MM3
C2M04M
D4M40M
E33MM0

然后连接E,最后D。  


所以我们的操作实际上就是不断的刷新维护第一列(行)数组。当然,为了清晰,我们也可以把其单独摘出来,或者用队列记录这一个集合等处理方式

#include<stdio.h>
#include<string.h>
#define INF 99999999
int V_nMapValue[30][30];
int V_nMinAns;
int n;
int fmin(int a,int b) 
{
	return a<b?a:b;
}
void Init()                             //初始化
{
	int i,j;

    for(i=0;i<30;i++)
    for(j=0;j<30;j++)
    V_nMapValue[i][j]=(i==j)?0:INF;
    
    V_nMinAns=0;
}


int Get_PointId_by_PointName(char c)
{
	return int(c-64);
}

void Prim()
{
	int Z_vis[30], i,j,k;
    memset(Z_vis, 0, sizeof(Z_vis));    
    Z_vis[1]=1;                                               //这里用Z数组标记该节点是否已经进入集合
    
	for(i=2;i<=n;i++)
	{
	    int Id_temp=0,Value_temp=INF;
	    for(j=2;j<=n;j++)
	    if(!Z_vis[j]&&V_nMapValue[1][j]<Value_temp)
        {
        Id_temp=j;
        Value_temp=V_nMapValue[1][j];
        }
        
        if(Value_temp==INF)                   
		break;
			
        V_nMinAns+=Value_temp;
	    Z_vis[Id_temp]=1;
	    
	    
	   // printf("%d~~%d~~%d\n",Id_temp,Value_temp,V_nMinAns);
	    
	    
	    for(k=2;k<=n;k++)
        if(!Z_vis[k])
        V_nMapValue[1][k]=fmin(V_nMapValue[1][k],V_nMapValue[Id_temp][k]); 
        
        
      //  for(j=1;j<=n;j++)
       // printf("%d%c",V_nMapValue[1][j],j==n?'\n':' ');
        
	
	}
}
	
	
	
	
	


int main()
{
	
char N_cStPoint,N_cEnPoint_temp;
int C_nListSum;	
int V_nSide_temp;
int Point_St_id,Point_En_id;
while(scanf("%d",&n)!=EOF&&n)
{
    getchar();
    Init();   
    
for(int i=1;i<n;i++)
{
  scanf("%c", &N_cStPoint);
  scanf("%d", &C_nListSum);
  while(C_nListSum--)
  {
      getchar();
      scanf("%c", &N_cEnPoint_temp);
      scanf("%d", &V_nSide_temp);
      Point_St_id=Get_PointId_by_PointName(N_cStPoint);
      Point_En_id=Get_PointId_by_PointName(N_cEnPoint_temp);
      V_nMapValue[Point_St_id][Point_En_id]=V_nMapValue[Point_En_id][Point_St_id]=V_nSide_temp;    

  }
  getchar();
}


/*
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
printf("%d%c",V_nMapValue[i][j],j==n?'\n':' ');
*/








     Prim();



printf("%d\n",V_nMinAns);
}
return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值