您的位置:首页 > 理论基础 > 数据结构算法

PAT 数据结构 06-图6. 公路村村通(30)Prim最小生成树算法

2015-07-16 19:31 411 查看
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。

输入格式说明:

输入数据包括城镇数目正整数N(<=1000)和候选道路数目M(<=3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。

输出格式说明:

输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出-1,表示需要建设更多公路。

样例输入与输出:

序号输入输出
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

2
3 1
2 3 2

-1

3
5 4
1 2 1
2 3 2
3 1 3
4 5 4

-1

用Prim最小生成树算法来写:

已吸收的结点为集合AB,一开始只有1号结点。未吸收的结点为集合UD,一开始包含除1号外的所有结点。每次以最小边的代价将UD中的一个结点吸收到AB。

一开始想到可以用穷举的方法写,当AB集合有k个结点时,UD集合有N-K个,穷举两个集合的所有可能连线,通常来说遍历的效率较低,说得形象点,k每次变化时,每次都要判断1号结点都要与UD集合中的所有结点是否有边,显然第一次之后的判断都是多余的。从这个角度来想,我们每次只要判断AB中最新的一个结点与UD中所有结点是否有边即可。

为了便于操作,定义结点结构体如下面代码所示。由于我是第一次写该算法,多加了前驱结点proNode,用于输出具体的生成树。该变量在本题中是多余的,可以去掉。

/*2015.7.16cyq*/
//Prim最小生成树算法
#include <iostream>
#include <vector>
#include <fstream>
#include <algorithm>
using namespace std;

const int MAX=2147483647;
//ifstream fin("case1.txt");
//#define cin fin

struct node{
int num;	//结点序号
int weight;	//能连通到已确定子图的最小边
int preNode;//最小边依附的结点,用于输出具体生成树
node():num(-1),weight(-1),preNode(-1){}
node(int n,int wei,int preN):num(n),weight(wei),preNode(preN){}
bool operator < (const node &a) const{
return weight<a.weight;
}
};

int main(){
int N,M;
cin>>N>>M;
vector<vector<int> > edges(N+1,vector<int>(N+1,-1));//-1表示不连通
int a,b,c;
while(M--){
cin>>a>>b>>c;
edges[a][b]=c;
edges[b][a]=c;
}

vector<node> AB;//已吸收的点,一开始只有结点1
AB.push_back(node(1,0,-1));//
vector<node> UD(N-1);//未吸收的点
for(int i=0;i<N-1;i++){
UD[i].num=i+2;//编号2到N
int tmp=edges[1][UD[i].num];
if(tmp>0){//能连到结点1的结点
UD[i].weight=tmp;
UD[i].preNode=1;
}else{//不能连到结点1的结点
UD[i].weight=MAX;
UD[i].preNode=-1;
}
}
int totalLength=0;
//vector<pair<int,int> > result;//用于输出具体生成树
while(!UD.empty()){
sort(UD.begin(),UD.end());
node cur=UD[0];//取出能以最小代价吸收的点
UD.erase(UD.begin());
if(cur.weight==MAX){
cout<<"-1"<<endl;
return 0;
}
totalLength+=cur.weight;
//	result.push_back(make_pair(cur.num,cur.preNode));
for(auto it=UD.begin();it!=UD.end();++it){//用该点修正未吸收的点
int w=edges[cur.num][(*it).num];
if(w>0){
if(w<(*it).weight){
(*it).weight=w;
(*it).preNode=cur.num;
}
}
}
}
cout<<totalLength<<endl;
//for(auto it=result.begin();it!=result.end();++it){//输出最小生成树
//	cout<<(*it).first<<" "<<(*it).second<<" "<<edges[(*it).first][(*it).second]<<endl;
//}
return 0;
}
事实上,可以进一步把UD集合分成两个集合,第一个集合中的点与AB相邻,第二个集合的点与AB不相邻,尚未探测到。这样每次只要对第一个集合进行排序,取出最小值进行操作即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: