最小生成树之新的开始(prim)

初学prim记录一下

 

 

先说说思想吧(emmm自言自语吧)

       可以在图中随便找一个点,然后向周围不断地找最小的边(也就是连接各点之间的杠杠)

这里有一点之前纠结过    就是它一直往里面加,结束的标志是什么啊,这就是(prim)函数外围的for(i)循环的意义了每一次循环都会有点加进去,有n个点循环n次就结束了。说是任意点都可以,其实一般一直都是从1点开始的,从其他点开始也不是不行,就是我感觉能把我迷一会儿。

来点核心的吧(main函数里这么长,,,除了那个dist初始化,基本就没什么重要的了)

      1.明明样例给的是4,为什么要从0开始便利

         (1)dist数组在我看来和一维的dp背包很像,但学长说只是个简单的贪心,dist数组里存的是这个点到其他点的距离,并且随着节点的变换在不断更新,若果遇到更小的就换掉,不然就保留,什么意思呢?就是比如1 和 2 都能到 3  那dist【3】就是 1或者2到3的距离,哪个小保存哪个。

          (2)从0开始便利是因为这些村子看看都是相连的,有的可能直接建发电站合适,有的可能需要联通,所以我们,在开局,i=0的时候,从1开始,把各个村子的建发电站的费用存到dist【】数组里,大的数会被之后的更小的数更新,只管存。
 

      2.prim函数里可以看成上下两层就从vis下的空格分开。上面的就是纯纯遍历,找一个最小值罢了,然后存到cost里,下面的就是找到最小值点后,再次更新dist数组,这个数组是一直传承下来的,所以不用再遍历一遍之前的点,从这个最小值点开始便利它所能接触的距离,如果有比dist数组中原来值小的,dist就会被更新

       3.dist初始化很重要,memset是个函数,0x3f3f3f3f是int中最大值得表示方式 ,不知道初始化有什么用的,,emmmm现在这种点全是联通的,确实有点体现不出来,但有的点和点之间是不通的,这时候无穷大就派上用场了。

也就这么多了,把建电厂的费用也加一组数据到图中g【0】【i】,用来比较费用哪个划算,这个prim,就上边遍历,下面一个小贪心,每次dist里只留到该点最小值,然后再去上面遍历一下,看看哪个值最小,就加到cost里。

要是,,,,真的很迷得话,,,带数进去,走一遍,,当初看不懂,还以为学长代码给错了呢,,但把数带进去自己走走,emmm感觉会好一点。

emmm,上代码吧

#include <stdio.h>
#include <iostream>
#include <algorithm> 
#include <string.h>
using namespace std; 
int g[500][500];
int dist[500];
int vis[500];
int a[500];
int n,xb;
int prim()
{
	dist[0] = 0;
	int cost = 0;
	for(int i = 0 ; i <= n ; i++)
	{
		int t = -1;
		for(int j = 0 ; j <= n ; j++)
		{
			if(!vis[j] && (t == -1 || dist[t] > dist[j]))
			{
				t = j; 
			}
		}
		cost += dist[t];
		vis[t] = 1;
		
		for(int j = 0 ; j <= n ; j++)
		{
			dist[j] = min(g[t][j],dist[j]);
		}
	}
	return cost;
}
int main()
{
	memset (dist,0x3f3f3f3f,sizeof(dist));
	memset (vis,0,sizeof(vis));
	scanf("%d",&n);
	int minn=500000;
	int sum=0;
	int w;
	for(int i = 1 ; i <= n ; i++)
	{
		scanf("%d",&w);
		g[0][i] = w;
		
	}
	for(int i = 1 ; i <= n ; i++)
	{
		for(int j = 1 ; j <= n ; j++)
		{
			scanf("%d",&g[i][j]);
		}
	}
	int t = prim();
	printf("%d",t);
	 
 } 

再来一个,,当时讲最小成树(prim)的学长的板子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2020;
int g[N][N];
int dist[N];
bool st[N];
int len = 0;
int n,m;
int prim()
{
	dist[1] = 0;
	for(int i=0;i<n;i++)
	{
		int t = -1;
		for(int j=1;j<=n;j++)
		{
			if(!st[j] && (t==-1 || dist[t]>dist[j]))
		    t = j;
		}
		if(i && dist[t]==0x3f3f3f3f) 
		{
		     return -1; //这句是用来判断是不是联通的,以便于及时退出
		}
		len += dist[t];
		st[t] = 1;
		for(int j=1;j<=n;j++)                //每次选出一个点后都更新一下周围的邻点,相当于把点集s看成是一个集合 
		{
			dist[j] = min(dist[j],g[t][j]);      //集合中的任意点都可以用来更新其周围的点,所以最后的结果就是集合中所有点能更新的都更新了之后,这点离整个集合的最小值 
		}
		
	}
	return len;
} 
int main()
{
	memset(dist,0x3f3f3f3f,sizeof dist);
	memset(g,0x3f3f3f3f,sizeof g);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		g[a][b] = g[b][a] = min(g[a][b],c);
	}
	int t = prim();
	if(t==-1) cout<<"impossible";
	else cout<<t;
	return 0;
}

就这些了,,,基本上都是我自己的想法,,有什么不足的望指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值