题目1017:还是畅通工程
时间限制:1 秒
内存限制:32 兆
特殊判题:否
提交:7348
解决:3657
-
题目描述:
-
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
-
输入:
-
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
-
输出:
-
对每个测试用例,在1行里输出最小的公路总长度。
-
样例输入:
-
3 1 2 1 1 3 2 2 3 4 4 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 0
-
样例输出:
-
3 5
-
答疑:
解题遇到问题?分享解题心得?讨论本题请访问:http://t.jobdu.com/thread-7741-1-1.html
思路:最小生成树问题,这里我是用并查集做的,也即是克鲁斯卡Kruskal算法,感觉特别简单。
#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
struct Road
{
int a; //a,b为一条边所连的二个顶点
int b;
int w; //边的权值
}road[5000];
int v[5000]; //定义并查集数组
bool cmp(const Road &a,const Road &b)
{
return a.w<b.w;
}
int getRoot(int a) //在并查集中查找根节点的函数
{
while(v[a]!=a)
a=v[a];
return a;
}
int main()
{
ifstream in;
in.open("3.txt");
int n;
int a,b;
int sum;
while(in>>n && n!=0)
{
for(int i=0;i<n*(n-1)/2;i++)
in>>road[i+1].a>>road[i+1].b>>road[i+1].w;
for(int i=1;i<=n;i++)
v[i]=i;
sort(road+1,road+n*(n-1)/2+1,cmp); //对road数组中的E条边按权值进行从小到大排序
sum=0;
for(int i=1;i<=n*(n-1)/2;i++)
{
a=getRoot(road[i].a);
b=getRoot(road[i].b);
if(a!=b)
{
v[a]=b;
sum+=road[i].w;
}
}
cout<<sum<<endl;
}
return 0;
}
(2) 给出别人写的Prime()普里姆算法
#include <iostream>
#include <vector>
using namespace std;
#define INF 0x7fffffff
int N;
vector<vector<int> > val;
int prim(){
vector<int>minV(N+1,INF);
vector<bool>vis(N+1,false);
int res=0;
minV[1]=0;
for(int i=1;i<=N;i++)
{
int j,k;
for(k=-1,j=1;j<=N;j++)
if(!vis[j]&&(k==-1||minV[j]<minV[k]))
k=j;
vis[k]=1;
res+=minV[k];
for(int i=1;i<=N;i++)
if(!vis[i]&&val[k][i]<minV[i])
minV[i]=val[k][i];
}
return res;
}
int main()
{
//freopen("C:\\in.txt","r",stdin);
while(cin>>N&&N){
val.assign(N+1,vector<int>(N+1,INF));
for(int i=1;i<=N*(N-1)/2;i++){
int a,b,d;
cin>>a>>b>>d;
val[a][b]=val[b][a]=d;
}
cout<<prim()<<endl;
}
return 0;
}
(3) 自己有亲自重新写了Prime普里姆算法
#include <iostream>
#include <cstring>
#include <fstream>
using namespace std;
const int INF=0x7fffffff; //表示边的权值无穷大
int edges[101][5000]; //存储树边的权值
int lowcost[101]; //存放当前生成树到剩余个顶点最短边的权值
int vset[101]; //表示顶点是否被列入生成树中
void Prime(int n,int v,int &sum);
int main()
{
ifstream in;
in.open("6.txt");
int n;
int sum;
while(in>>n && n!=0)
{
for(int i=1;i<=n;i++) //初始化边的权值
for(int j=1;j<=n*(n-1)/2;j++)
edges[i][j]=INF;
for(int i=0;i<n*(n-1)/2;i++)
{
int j,k;
in>>j>>k;
in>>edges[j][k];
edges[k][j]=edges[j][k]; //针对无向图应该行列都要赋值
}
Prime(n,1,sum);
cout<<sum<<endl;
}
return 0;
}
void Prime(int n,int v,int &sum)
{
int i,j,k,min;
for(i=1;i<=n;i++)
{
lowcost[i]=edges[v][i];
vset[i]=0;
}
vset[v]=1;
sum=0;
for(i=1;i<n;i++)
{
min=INF;
for(j=1;j<=n;++j)
if(vset[j]==0 && lowcost[j]<min) //循环用于选出候选边的最小者
{
min=lowcost[j];
k=j;
}
vset[k]=1;
v=k;
sum+=min;
for(j=1;j<=n;++j) //这个循环以刚并入的顶点v为媒介更新候选边
if(vset[j]==0 && edges[v][j]<lowcost[j])
lowcost[j]=edges[v][j];
}
}