H - 畅通工程
Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u
Submit
Status
use MathJax to parse formulas
Description
省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。经过调查评估,得到的统计表中列出了有可能建设公路的若干条道路的成本。现请你编写程序,计算出全省畅通需要的最低成本。
Input
测试输入包含若干测试用例。每个测试用例的第1行给出评估的道路条数 N、村庄数目M ( < 100 );随后的 N
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。
Output
对每个测试用例,在1行里输出全省畅通需要的最低成本。若统计数据不足以保证畅通,则输出“?”。
Sample Input
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
0 100
Sample Output
3
?
第一种:
kruskal
转自大佬博客:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175
此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
1. 把图中的所有边按代价从小到大排序;
2. 把图中的n个顶点看成独立的n棵树组成的森林;
3. 按权值从小到大选择边,所选的边连接的两个顶点
ui,vi,ui,vi
,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。
代码:(并查集的应用)
因为题目要求任意两个城市之前都得联通
所以要用以下代码
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
struct node{
int from,to,len;
}s[105];
int pre[105];
int ans;
void init(int m)
{
for(int i=1;i<=m;i++)
pre[i]=i;
}
int fi(int n)
{
int r=n;
while(r!=pre[r])
r=pre[r];
return r;
}
int mer(int a,int b)
{
int a1,b1;
a1=fi(a);
b1=fi(b);
if(a1!=b1)
{
pre[b1]=a1;
return 1; //两个数的祖先不同,即不属于同一棵树,所以把这两个城市之间的距离加进去
}
return 0;
}
bool cmp(node a,node b)
{
return a.len<b.len;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
ans=0;
int flag=1;
if(n==0)
break;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&s[i].from,&s[i].to,&s[i].len);
}
sort(s+1,s+n+1,cmp);
init(m);
for(int i=1;i<=n;i++)
{
int sum=mer(s[i].from,s[i].to);
if(sum==1)
{
ans+=s[i].len;
flag++;
}
}
if(flag==m) //只有这时才能证明任意两城市联通
printf("%d\n",ans);
else
printf("?\n");
}
return 0;
}
第二种:prim
每次找已选的集合和未选的集合之间任意两点的连线的最小值,每次加进去
图片转自dalao博客:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175 嘻嘻嘻
代码:
#include <iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define INF 0x3f3f3f3f
using namespace std;
int vis[105];
int cost[105][105];
int low[105];
int ans;
void prim(int m)
{
for(int i=1;i<=m;i++)
{
low[i]=INF; //low函数初始化
vis[i]=0; //0是未找的1是已找的
}
for(int i=1;i<=m;i++) //i的取值取决于城市编号
low[i]=cost[1][i];
vis[1]=1;
low[1]=0;
for(int i=1;i<m;i++)
{
int minc=INF;
int p=-1;
for(int j=1;j<=m;j++)
if(minc>low[j]&&!vis[j]) //找low数组中的最小值
{
minc=low[j];
p=j;
}
if(minc==INF) //两城市之间未连通
{
printf("?\n");
return ;
}
ans+=minc;
vis[p]=1;
for(int j=1;j<=m;j++)
if(!vis[j]&&low[j]>cost[p][j]) //更新low数组与未连接的点之间的最小值
low[j]=cost[p][j];
}
printf("%d\n",ans);
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
ans=0;
int a,b,c,sum;
memset(cost,0x3f3f3f3f,sizeof(cost));
if(n==0)
break;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&a,&b,&c);
if(c<cost[a][b])
cost[a][b]=cost[b][a]=c; //找两点之间的最短路
}
prim(m);
}
return 0;
}