现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。
输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12
做这个题的经历还是有点艰辛的,第一遍数据结构不好好学的下场就是后面要还的。
听完姥姥的课兴致冲冲的去写Prim,第一遍是莫名奇妙的Bug,漏洞百出。。。。。
主要思路:这是一道关于最小生成树(MST)的题目,主要利用贪心思想,每次选取最优顶点加入集合,直到所有顶点都加入。
首先要解决的图是一个无向网图,有两个集合,一个是(里面存放的是已经加入MST的顶点),另一个集合是剩下没有加入MST的顶点集合。 初始是有n个顶点,m条边。选取编号1为源点。
lowcost[ i ] 顶点i距MST的最短路,lowcost[ i ]=0表示i已经加入了MST中
weight[ i ][ j ] 顶点i距j的距离
parent[ i ] i的父亲节点,用于打印整棵树。
1,首先从剩下节点的集合中每次选取距MST(即已经加入节点的集合)最少费用(lowcost)的那个顶点,这里的距离一定是距整棵树的最短距离,即树中的每个节点到剩下节点的最短路。
2,每加入一个新的顶点w后,都要查看与该顶点相邻的节点 j 的lowcost是否发生变化,即Weight [w] [j]<lowcost [j],并且j是没有加入MST中的。开始有点不太理解为啥更新lowcost而不是weight,因为整个过程就是在和lowcost打交道啊
3,重复步骤1,2。 正常情况下n-1次(因为不包括源点),特殊情况就是该图不连通就直接跳出。
还有就是初始化的问题,这个问题开始时困扰了我蛮久,导致后面BUG百出........
weight 开始时将与源点有直接边的顶点置为边的权重,其它置为正无穷。
lowcost用weight初始化(结果是源点的邻接点的lowcost为直接边的权重,其它为无穷大)并且一定要记得将lowcost[ 1 ]=0,
将源点的parent置为-1。
今天还学习到了无穷大0x3f3f3f3f,一个好处就是它加上另一个无穷大的值一般也不会溢出的。
先放一个第一遍写的漏洞百出的代码 再和后面的作比较。
#include<bits/stdc++.h>
#define maxsize 1005
#define inf 99999990
using namespace std;
int dist[maxsize];/*某节点到最小生成树的最短距离,
初始时把源点的邻接点的DIST置为w[s][i],不临接为inf*/
int weight[maxsize][maxsize];
int parent[maxsize];
int n,m,sum,v,min_flag,cnt;
int c1,c2,d,s;
void prim();
void pre_deal();
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&c1,&c2,&d);
weight[c1][c2]=d;
weight[c2][c1]=d;
}
s=c1;
pre_deal();
prim();
return 0;
}
void pre_deal()
{
s=1;
for(int i=1;i<=n;i++)
{
if(weight[s][i])
dist[i]=weight[s][i];
else
dist[i]=inf;
}
dist[s]=0;
parent[s]=-1;
}
void prim()
{
for(int i=1;i<n;i++)
{
min_flag=inf;
for(int j=1;j<=n;j++)
{
if(dist[j]&&weight[s][j]&&dist[j]<min_flag)
{
min_flag=dist[j];
v=j;
}
}
if(i==1)
parent[v]=s;
dist[v]=0;
for(int w=1;w<=n;w++)
{
if(dist[w]&&(weight[v][w]<dist[w])&&weight[v][w])
{
dist[w]=weight[v][w];
parent[w]=v;
}
}
}
for(int i=1;i<=n;i++)
{
if(!dist[i])
cnt++;
}
if(cnt!=n)
printf("-1\n");
else
{
for(int i=1;i<=n;i++)
{
if(parent[i]!=-1)
sum+=weight[i][parent[i]];
}
printf("%d\n",sum);
}
}
确实很Low
#include<bits/stdc++.h>
#define maxsize 1005
#define inf 0x3f3f3f3f
using namespace std;
int lowcost[maxsize];
int weight[maxsize][maxsize];
int parent[maxsize];
int prim();
int find_min();
int n,m;
int main()
{
memset(weight,0x3f,sizeof(weight));///在开始就初始化好weight
scanf("%d%d",&n,&m);
int c1,c2,d;
for (int i = 0;i < m; i++)
{
scanf("%d%d%d", &c1, &c2, &d);
weight[c1][c2] = weight[c2][c1] = d;
}
printf("%d\n", prim());
return 0;
}
int find_min()///寻找最小顶点
{
int minTemp=inf;
int v=0; ///巧妙之处啊啊啊,若图不连通就返回0,否则就返回最小顶点。
for(int j = 1;j<= n;j++)///直接判断lowcost一次。
{
if(lowcost[j]&&lowcost[j]<minTemp)///判断顶点未加入,且选lowcost最小的。
{
minTemp = lowcost[j];
v = j;
}
}
return v;
}
int prim()
{
parent[1] = -1;
int ans = 0;
for(int i = 1; i <= n; i++)
lowcost[i] = weight[1][i];
lowcost[1] = 0;///一定要记得!!!
for(int i = 2; i <= n; i++)
{
int k = find_min();
if(k)///巧妙之处啊啊啊
{
if( i == 2) parent[k] = 1;
ans += lowcost[k];
lowcost[k] = 0;
for(int w = 1; w <= n; w++)
{
if(lowcost[w] && weight[k][w] < lowcost[w])///更新k的邻接点的lowcost,
///更新那些inf和更小的值。
{
lowcost[w] = weight[k][w];
parent[w] = k;
}
}
}
else return -1;///图不连通返回-1
}
return ans;
}
学习了一位大牛的代码风格看起来也舒服多了啊。。。