Time Limit: 3000MS | Memory Limit: 30000K | |
Total Submissions: 11929 | Accepted: 3171 |
Description
Input
Output
Sample Input
1 3 3 1 2 3 1 3 4 2 3 5
Sample Output
Scenario #1: 4
题目大意是就是何处一个图,n个顶点和m条边,每个边都有最大承载量,现在我要从1点运送货物到n点,求能运送货物的最大重量。
对于数据,第一行为t代表测试数据个数,第二行为n和m(意义见上),接着m行,每行三个整数分别是代表一条边的起点,终点及最大承重量。输出能运送货物的最大重量,格式见样例。注意数据输完后还要再多输一个空行。
对于数据,从1运到3有两种方案
方案1:1-2-3,其中1-2承重为3,2-3承重为5,则可以运送货物的最大重量是3(当大于3时明显1到不了2)
方案2:1-3,可知1-3承重为4,故此路可运送货物的最大重量是4,故答案输出4
因为此前也没做过图论题,对一些算法都不熟,再刚开始题意理解有问题,WA了几次,看懂题意后,搜了下别人的解题报告,说是Dijkstra的变形或这求最大生成树。也许对Dijkstra运用(压根就没用过)的不是很熟,一直不知道怎么下手,连样列都过不了。后就直接转到求最大生成树上去了,网上大部分代码是Prim算法,由于《算法入门竞赛经典》书没介绍该算法,暂时还没看,所以就选择Kruskal求最大生成树。然后选择Kruskal的一个问题就是连通分量的处理,《入门经典》是用的并查集来处理,因为对生成树算法不是很熟,就直接套的上面的模板。然后题目就是编程了求最大生成树,并找出从1-n的最小权值的边。当然,这棵树不用搜完,因为,你从1到n不一定会每一个节点都走过,当将1-n连通时此时的权值就是所求的值;转换用Kruskal时因为数组开大了MLE一次,开小了RE一次,最后决定还是动态分配靠谱些。不过因为一个小细节又WA了一次,最后改正,终于AC了,你说,AC一题我容易不!!!总之ACM搞图论的上辈子都是折翼的天使!!!
如果有时间,这题还会再做一遍的,用Prim算法和Dijkstra试一下!
参考代码:
1 //第一次提交的代码基本是套模板的,和自己写的出入较大,不习惯,将代码修改下感觉也许更好!,第一次提交的代码见最下面
2 #include<iostream>
3 #include<cstdlib>
4 #include<cstdio>
5 #include<cstring>
6 #include<algorithm>
7 #include<cmath>
8 using namespace std;
9 const int inf = (1<<20);
10 int p[1005]; //p是用于并查集的,r是用来存储边序号的
11 struct prog {
12 int w,v,u; //记录起点,终点,权值
13 };
14 bool cmp(prog a,prog b)
15 {//间接排序函数,
16 return a.w>b.w;
17 }
18 int find(int x)
19 {//并查集里的find函数,你懂的
20 return p[x]==x?x:p[x]=find(p[x]);
21 }
22 int main()
23 {
24 int t;
25 cin >> t;
26 int k = 1;
27 while(t--)
28 {
29 int n ,m;
30 cin >> n >> m;
31 prog *r;
32 r=new prog[m];
33 int i ;
34 for ( i = 0 ; i < m ; i ++ )
35 cin>>r[i].u>>r[i].v>>r[i].w; //输入边的信息
36
37 for ( i =1 ; i <= n; i ++ )
38 p[i]=i;//初始化并查集
39
40 sort(r,r+m,cmp);//根据边的权值的大小将边的序号进行排序,r[i]表示第i+1大的边存储在u,v,w数组中的序号
41 int ans=inf; //将答案初始化为最大值
42 for ( i = 0 ; i < m ; i ++ )
43 {
44 int x=find(r[i].u);
45 int y=find(r[i].v);
46 if(x!=y)
47 {//如果该边所在的两边不在同一个连通分量里,则连接该边
48 if(ans>r[i].w)//如果该边的权值比ans小(实际上一定不会比ans大),则更新ans
49 ans=r[i].w;
50 p[x]=y;//连接该边
51 if(find(1)==find(n))//当1和n连通时,则说明找到了一条从1到n的路,并且可知该路的所有边的权值都是最大的,故边的最小权值就是答案
52 break;
53 }
54 }
55 //输出答案,格式如题所述
56 cout<<"Scenario #"<<k<<":"<<endl;
57 cout<<ans<<endl<<endl;
58 k++;
59 }
60 return 0;
61 }
附:第一次参考代码
1 #include<iostream>
2 #include<cstdlib>
3 #include<cstdio>
4 #include<cstring>
5 #include<algorithm>
6 #include<cmath>
7 using namespace std;
8 const int inf = (1<<20);
9 int *p,*r; //p是用于并查集的,r是用来存储边序号的
10 int *u,*v,*w; //分别代表边的起点,终点,和权值,明显不是我的风格,先熟悉下模板,不得不这样写
11 bool cmp(const int a,const int b)
12 {//间接排序函数,
13 return w[a]>w[b];
14 }
15 int find(int x)
16 {//并查集里的find函数,你懂的
17 return p[x]==x?x:p[x]=find(p[x]);
18 }
19 int main()
20 {
21 int t;
22 cin >> t;
23 int k = 1;
24 while(t--)
25 {
26 int n ,m;
27 cin >> n >> m;
28 int i ;
29 u=new int[m];
30 v=new int[m];
31 w=new int[m];
32 r=new int[m];//动态分配
33 for ( i = 0 ; i < m ; i ++ )
34 {
35 int a , b , c ;
36 cin>>a>>b>>c;
37 u[i]=a;
38 v[i]=b;
39 w[i]=c;//加入边
40 }
41 p=new int[n+1];
42 for ( i =1 ; i <= n; i ++ )
43 p[i]=i;//初始化并查集
44 for ( i = 0 ;i < m ; i ++ )
45 r[i]=i;//初始化边序号
46 sort(r,r+m,cmp);//根据边的权值的大小将边的序号进行排序,r[i]表示第i+1大的边存储在u,v,w数组中的序号
47 int ans=inf; //将答案初始化为最大值
48 for ( i = 0 ; i < m ; i ++ )
49 {
50 int e=r[i];//找到第i+1大的边
51 int x=find(u[e]);
52 int y=find(v[e]);
53 if(x!=y)
54 {//如果该边所在的两边不在同一个连通分量里,则连接该边
55 if(ans>w[e])//如果该边的权值比ans小(实际上一定不会比ans大),则更新ans
56 ans=w[e];
57 p[x]=y;//连接该边
58 if(find(1)==find(n))//当1和n连通时,则说明找到了一条从1到n的路,并且可知该路的所有边的权值都是最大的,故边的最小权值就是答案
59 break;
60 }
61 }
62 //输出答案,格式如题所述
63 cout<<"Scenario #"<<k<<":"<<endl;
64 cout<<ans<<endl<<endl;
65 k++;
66 }
67 return 0;
68 }