初学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;
}
就这些了,,,基本上都是我自己的想法,,有什么不足的望指教。