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 }

 

 
posted @ 2018-07-11 12:27  霜雪千年  阅读(746)  评论(0编辑  收藏  举报