ACM程序设计选修课——Problem E:(ds:图)公路村村通(Prim)
问题 E: (ds:图)公路村村通
时间限制: 1 Sec 内存限制: 128 MB提交: 9 解决: 5
题目描述
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。
输入
第一行为整数T,表示有T个case(测试实例)。
接下来每个case包含:
输入数据包括城镇数目正整数N(<=1000)和候选道路数目M(<=3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。
输出
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出-1,表示需要建设更多公路。
样例输入
3
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
3 1
2 3 2
5 4
1 2 1
2 3 2
3 1 3
4 5 4
样例输出
12
-1
-1
Prim算法对当前连通图的不断扩展,每次对扩展的头进行比较,对最小边权的那个点继续进行扩展,直到无法再扩展或所有点均被访问过。而优先队列是可以选出最小边权的一条边,并对它的两头进行扩展,符合Prim算法的思想,但是这样的算法会把所有的关联边压入队列,那怎么保证比较次数尽量小?即选到了某个点关联最小边权的边就把其他关联边全舍弃呢?因此要增加一个vis数组,来记录这个点是否被“松弛操作过”(有点迪杰斯特拉的意思)因此边进入这个队列不会多次,由于不像迪杰斯特拉那样可能多次地进行对边进行松弛操作,大多数情况下一次就够了,因此适合稠密图,而Kruskal适合稀疏图。
代码:
#include<iostream> #include<algorithm> #include<cstdlib> #include<sstream> #include<cstring> #include<cstdio> #include<string> #include<deque> #include<stack> #include<cmath> #include<queue> #include<set> #include<map> #define INF 0x3f3f3f3f #define MM(x) memset(x,0,sizeof(x)) using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N=1010; struct info { int p; int to; int dx; info(int t,int d):to(t),dx(d){} bool operator<(const info &a)const { return dx>a.dx; } }; vector<info>E[N]; int vis[N]; void init() { for (int i=0; i<N; i++) E[i].clear(); MM(vis); } int main(void) { int n,m,x,y,z,i,j,k; int tcase; scanf("%d",&tcase); while (tcase--) { init(); scanf("%d%d",&n,&m); for (i=0; i<m; i++) { scanf("%d%d%d",&x,&y,&z); E[x].push_back(info(y,z)); E[y].push_back(info(x,z)); } priority_queue<info> Q; for (i=0; i<E[1].size(); i++) Q.push(E[1][i]); vis[1]=1; int ans=0; while (!Q.empty()) { info temp=Q.top(); Q.pop(); if(!vis[temp.to]) { vis[temp.to]=1; ans+=temp.dx; for (i=0; i<E[temp.to].size(); i++) Q.push(E[temp.to][i]); } } bool flag=1; for (i=1; i<=n; i++) { if(vis[i]==0) { flag=0; break; } } flag?printf("%d\n",ans):puts("-1"); } return 0; }