goj 他和她(最大生成树+最短路径)
Problem Description:
大二上学期刚过完,平时成绩不错的小V参加了一个小型编程比赛,遇到一道题,虽然是书上的却不会做。于是找了在机房的他帮忙。 他:什么题目这么厉害,书上有却不会? 小V:就是给若干个城市编号,也告诉了我连接这些城市的公路的距离,也有一些城市间是没有直接的路的。求从一个城市到另外一个城市的最短距离。 他:这不很简单的单向最短路径么?照书敲都行啦。 小V:不是什么比赛都能带书。 他:那你自己敲出来还不行么? 小V:不会敲..完全下不了手,我只会考试那种画图的,写代码的话连思路都没有。 他:好吧- -!思路是这样的: s为起点,w[u,v]为点u和v之间的边的长度(无边则长度设为一个很大的数,只要比w[]的最大值大就行),把s点到其他点的距离保存在一个数组里,假设是dis[]。 1.初始化:起点到起点的距离dis[s]设为0,s点到其他点(v)的距离设w[s,v],同时把所有的点都标记为未访问过,s点标记访问过。 2.循环n-1次: ①在没有访问过的点中取dis[]距离最小且未访问的点u,并标记为已访问。 ②对于每个与u相邻的点v,执行Relax(u,v),也就是说,如果满足u点在最短路径上的距离+u到v点距离小于原本v点到最短路径上的距离就把v点到最短路径的距离更新成更短的距离。 3.结束。此时对于任意的u,dis[u]就是s到u的距离。 小V:标记? 他:开个数组表示就行了。 小V:那我试敲下。 他:等会,顺便先判断下这些城市能不能从任意一个城市i到另外一个城市j吧。 小V:生成树? 他:这么聪明?不过判断连通不一定要生成树,你还可以用搜索、并查集,搜索就看能不能一次就搜遍,集合就看是不是它们在同一连通分量,不是就把它们合并在一个集合里,最终判断是不是全部点同属一个集合就行。 小V:那个叫什么并查集的怎么听起来跟克鲁斯卡尔算法那么像。 他:kruskal是可以用并查集优化的。不过既然你这么说了,那就干脆也求个最小生成树的值吧,即用公路长度和最小的n-1个公路将n个城市连在一起,求这n-1条路的公路长度。 小V:那代码上跟最短路那个算法有什么区别? 他:改下Relax()的条件,每加入一个点u,看其他跟该点有边的点v,v到树上的距离是不是小于u到树上源点的距离,是就更新,再把生成树上那些点的权值加起来就是答案。 大概半小时后,小V敲得差不多了。 他:看你都敲这么多了,也告诉你这么多了,不如加个难度,改求最大生成树,这样,我给你4个例子,你对照下答案。 经过比对,答案还是不对。这时他喵了一眼就看出是初始化问题,但他没有告诉她。因为他知道这是她必须跨过的坎,有多少学生能把各种算法的原理讲得很流畅,却写不出代码。 而对于她,他希望她能解决的不只是这个问题,因为这样才能实现他的心愿:一!起!刷!题! Hints:不要轻易放弃,水题虽多,AC不易。
Input:
多组数据,每组数据第一行是两个数N,M(1<=N<=100,0<=M<=1000),N表示城市个数,城市从1开始编号,M表示公路数目,编号为1的城市为起点,编号为N的为终点,接下来有M行,每行两个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示有一条长度为C的公路连接AB两城市。数据保证1号城市不是孤立的。
Output:
如果从1号城市出发,不能到其他所有城市,则输出-1,否则按样例格式输出最大生成树的值和从1号城市到N号城市的最短距离。
Sample Input:
3 3 1 2 45 1 3 20 3 1 10 3 1 1 2 5 2 2 2 1 250 1 2 520 4 7 1 4 88 2 4 1 4 2 50 3 1 6 4 1 1000 4 3 30 2 3 100
Sample Output:
spanning tree:65 shortest distance:10 -1 spanning tree:520 shortest distance:250 spanning tree:1150 shortest distance:36
解题思路:最大生成树即将prim算法中每次找最小权值改为找最大权值,每加入一个点更新一下用最大权值代替原来的最小值即可。这题还要考虑重边的情况,简单处理,简单水过。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=105; 4 const int INF=0x3f3f3f3f; 5 int n,m,a,b,c,maxcost[maxn],dist[maxn],G[maxn][maxn],cost[maxn][maxn];bool vis[maxn],used[maxn]; 6 int prim(){//最大生成树 7 for(int i=1;i<=n;++i)maxcost[i]=G[1][i]; 8 maxcost[1]=0;used[1]=true; 9 int res=0; 10 for(int i=1;i<n;++i){ 11 int k=-1; 12 for(int j=1;j<=n;++j) 13 if(!used[j]&&(k==-1||maxcost[k]<maxcost[j]))k=j;//每次找最大边权 14 if(k==-1)break; 15 used[k]=true;res+=maxcost[k]; 16 for(int j=1;j<=n;++j) 17 if(!used[j])maxcost[j]=max(maxcost[j],G[k][j]);//更新周围点到已经归纳点的集合的最小权值 18 } 19 return res; 20 } 21 int dijkstra(){//最短路径 22 for(int i=1;i<=n;++i)dist[i]=cost[1][i]; 23 dist[1]=0;vis[1]=true; 24 for(int i=1;i<n;++i){ 25 int k=-1; 26 for(int j=1;j<=n;++j) 27 if(!vis[j]&&(k==-1||dist[k]>dist[j]))k=j; 28 if(k==-1)break; 29 vis[k]=true; 30 for(int j=1;j<=n;++j) 31 if(!vis[j])dist[j]=min(dist[j],dist[k]+cost[k][j]); 32 } 33 return dist[n]; 34 } 35 int main(){ 36 while(~scanf("%d%d",&n,&m)){ 37 memset(vis,false,sizeof(vis));//全部初始化为未访问状态即false 38 memset(used,false,sizeof(used)); 39 for(int i=1;i<=n;++i){ 40 for(int j=1;j<=n;++j){ 41 G[i][j]=(i==j?0:-INF);//求最大生成树:初始化为最小值-INF 42 cost[i][j]=(i==j?0:INF);//求最短路径:初始化为最大值INF 43 } 44 } 45 for(int i=1;i<=m;++i){ 46 scanf("%d%d%d",&a,&b,&c); 47 G[a][b]=G[b][a]=max(G[a][b],c);//去重,代替最小权值 48 cost[a][b]=cost[b][a]=min(cost[a][b],c);//去重,代替最大权值 49 } 50 int ok=prim(); 51 if(ok<=0)cout<<-1<<endl;//如果不大于0,说明不能从1到达n,直接输出-1 52 else cout<<"spanning tree:"<<ok<<" shortest distance:"<<dijkstra()<<endl; 53 } 54 return 0; 55 }