NYOJ 118 修路方案

修路方案

时间限制:3000 ms  |  内存限制:65535 KB

难度:5

描述

南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路。

现在已经知道哪些城市之间可以修路,如果修路,花费是多少。

现在,军师小工已经找到了一种修路的方案,能够使各个城市都联通起来,而且花费最少。

但是,南将军说,这个修路方案所拼成的图案很不吉利,想让小工计算一下是否存在另外一种方案花费和刚才的方案一样,现在你来帮小工写一个程序算一下吧。

输入
第一行输入一个整数T(1<T<20),表示测试数据的组数
每组测试数据的第一行是两个整数V,E,(3<V<500,10<E<200000)分别表示城市的个数和城市之间路的条数。数据保证所有的城市都有路相连。
随后的E行,每行有三个数字A B L,表示A号城市与B号城市之间修路花费为L。
输出
对于每组测试数据输出Yes或No(如果存在两种以上的最小花费方案则输出Yes,如果最小花费的方案只有一种,则输出No)
样例输入
2
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
样例输出
No
Yes
来源
POJ题目改编
上传者
张云聪

解题:次小生成树,搞了好久,好多文档看不懂啊。。。只好学点奇葩的东西,走点旁门左道了。。。。。弱菜有弱菜的学习方法。。。

这是什么算法。。。?好吧。。。偷学于豆丁上一篇文章<<A-star和第k短路和次小生成树和Yen和MPS寻路算法>>

首先求出原图的最小生成树,记录权值之和为Minst.枚举添加每条不在最小生成树上的边<u,v>,加上以后一定会形成一个环,找到环上权值第二大的边(即除<u,v>外最大的边)把它删除掉,计算当前生成树的权值之和。取所有枚举修改的生成树权值之和的最小值,就是次小生成树。具体实现时,更简单的方法是从每个节点i遍历整个最小生成树,定义F[j]为从i到j的路径上最大边的权值。遍历图求出F[j]的值,然后对于添加每条不在最小生成树中的边<i,j>,新的生成树权值之和就是Minst-w<i,j>-F[j],记录其最小值,则为次小生成树。该算法的时间复杂度为O(n^2+m)。由于只用求一次最小生成树,可以用最简单的Prim算法,时间复杂度为O(n^2)。算法的瓶颈不在于最小生成树,而在于O(n^2+m)的枚举加边修改,所以用更好的最小生成树算法是没有必要的。


 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5 #include <vector>
 6 #include <climits>
 7 #include <algorithm>
 8 #include <cmath>
 9 #define LL long long
10 #define INF 0x3f3f3f
11 using namespace std;
12 struct arc {
13     int u,v,w;
14 } e[200010];
15 int mp[501][501],d[501],pre[501],uf[501];
16 int n,m;
17 bool vis[200010];
18 bool cmp(const arc &x,const arc &y){
19     return x.w < y.w;
20 }
21 int findF(int x){
22     if(x != uf[x])
23         uf[x] = findF(uf[x]);
24     return uf[x];
25 }
26 int kruskal(){
27     int i,j,ans = 0;
28     for(i = 1; i <= n; i++){
29         uf[i] = i;
30         pre[i] = -1;
31     }
32     memset(vis,false,true);
33     for(i = 0; i < m; i++){
34         int x = findF(e[i].u);
35         int y = findF(e[i].v);
36         if(x != y){
37             uf[x] = y;
38             ans += e[i].w;
39             pre[e[i].v] = e[i].u;
40             vis[i] = true;
41         }
42     }
43     return ans;
44 }
45 int main() {
46     int ks,i,j,Minst,mx,u,v;
47     bool flag;
48     scanf("%d",&ks);
49     while(ks--) {
50         scanf("%d%d",&n,&m);
51         for(i = 0; i < m; i++) {
52             scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
53             if(e[i].u > e[i].v) swap(e[i].u,e[i].v);
54             mp[e[i].u][e[i].v] = mp[e[i].v][e[i].u] = e[i].w;
55         }
56         sort(e,e+m,cmp);
57         Minst = kruskal();
58         flag = false;
59         for(i = 0; i < m; i++){
60             if(!vis[i]){
61                 mx = 0;
62                 u = e[i].u;
63                 v = e[i].v;
64                 while(pre[v] != u && pre[v] != -1){
65                     if(mp[v][pre[v]] > mx) mx = mp[v][pre[v]];
66                     v = pre[v];
67                 }
68                 if(mx == e[i].w){
69                     flag = true;break;
70                 }
71             }
72         }
73         flag?puts("Yes"):puts("No");
74     }
75     return 0;
76 }

 别人写的Prim算法版的次小生成树

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 using namespace std;
 5 #define maxN 510
 6 #define MAX 0x0fffffff
 7 #define MIN -0x0fffffff
 8 int N,M,map[maxN][maxN],dis[maxN],maxlen[maxN][maxN],pre[maxN];
 9 bool vis[maxN];
10 int prim() {
11     int i,j,k,minn,pr;
12     memset(vis,false,sizeof(vis));
13     for(i=1; i<=N; i++) {
14         dis[i]=map[1][i];
15         pre[i]=1;
16     }
17     vis[1]=true;
18     for(j=1; j<N; j++) {
19         minn = MAX;
20         for(i=1; i <= N; i++)
21             if(!vis[i] && dis[i]<minn) {
22                 minn=dis[k=i];
23             }
24         pr = pre[k];
25         maxlen[k][pr] = maxlen[pr][k] = map[k][pr];
26         for(i = 1; i <= N; i++)
27             if(vis[i])
28                 maxlen[i][k]=maxlen[k][i]=max(maxlen[i][pr],maxlen[pr][k]);
29         vis[k]=true;
30         for(i=1; i<=N; i++)
31             if(!vis[i]&&dis[i]>map[k][i]) {
32                 dis[i]=map[i][k];
33                 pre[i]=k;
34             }
35     }
36     for(i=1; i < N; i++)
37         for(j = i+1; j <= N; j++)
38             if(pre[i] == j|| pre[j] == i) continue;
39             else if(maxlen[i][j] == map[i][j]) return 1;
40     return 0;
41 }
42 int main() {
43     int T;
44     scanf("%d",&T);
45     while(T--) {
46         int u,v,w;
47         scanf("%d%d",&N,&M);
48         for(int i = 0; i <= N; i++)
49             for(int j=0; j <= N; j++) {
50                 map[i][j] = MAX;
51                 maxlen[i][j] = MIN;
52             }
53         for(int i = 0; i < M; i++) {
54             scanf("%d%d%d",&u,&v,&w);
55             map[u][v]=map[v][u]=w;
56         }
57         if(prim())printf("Yes\n");
58         else printf("No\n");
59     }
60     return 0;
61 }


 

NYOJ 118 修路方案,布布扣,bubuko.com

时间: 2024-10-26 20:18:21

NYOJ 118 修路方案的相关文章

NYOJ 118 路方案(第二小的跨越)

修路方案 时间限制:3000 ms  |  内存限制:65535 KB 难度:5 描写叙述 南将军率领着很多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N.因为交通不太便利,南将军准备修路. 如今已经知道哪些城市之间能够修路.假设修路,花费是多少. 如今.军师小工已经找到了一种修路的方案,可以使各个城市都联通起来,并且花费最少. 可是.南将军说,这个修路方案所拼成的图案非常不吉利,想让小工计算一下是否存在第二种方案花费和刚才的方案一样,如今你来帮小工写一个程序算一下吧. 输入 第一

NYOJ修路方案【次小生成树】

修路方案 时间限制:3000 ms  |  内存限制:65535 KB 难度:5 描述 南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路. 现在已经知道哪些城市之间可以修路,如果修路,花费是多少. 现在,军师小工已经找到了一种修路的方案,能够使各个城市都联通起来,而且花费最少. 但是,南将军说,这个修路方案所拼成的图案很不吉利,想让小工计算一下是否存在另外一种方案花费和刚才的方案一样,现在你来帮小工写一个程序算一下吧. 输入 第一行输

修路方案(nyoj)

算法:次小生成树 描述 南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路. 现在已经知道哪些城市之间可以修路,如果修路,花费是多少. 现在,军师小工已经找到了一种修路的方案,能够使各个城市都联通起来,而且花费最少. 但是,南将军说,这个修路方案所拼成的图案很不吉利,想让小工计算一下是否存在另外一种方案花费和刚才的方案一样,现在你来帮小工写一个程序算一下吧. 输入 第一行输入一个整数T(1<T<20),表示测试数据的组数每组测试数据的

修路方案(次小生成树)

修路方案 时间限制:3000 ms  |  内存限制:65535 KB 难度:5 描述 南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路. 现在已经知道哪些城市之间可以修路,如果修路,花费是多少. 现在,军师小工已经找到了一种修路的方案,能够使各个城市都联通起来,而且花费最少. 但是,南将军说,这个修路方案所拼成的图案很不吉利,想让小工计算一下是否存在另外一种方案花费和刚才的方案一样,现在你来帮小工写一个程序算一下吧. 输入 第一行输

nyoj 修路方案 (判断最小生成树是否唯一)

裸 1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 using namespace std; 5 6 struct node 7 { 8 int x, y, dis; 9 int flag; 10 } a[200010]; 11 12 int cmp(node x, node y) 13 { 14 return x.dis<y.dis; 15 } 16 int father[5

UVa 1151 买还是建

https://vjudge.net/problem/UVA-1151 题意: 平面上有n个点,你的任务是让所有n个点连通.为此,你可以新建一些边,费用等于两个端点的距离平方和.另外还有q个套餐可以购买,如果你购买了第i个套餐,该套餐中的所有结点都变得相互连通,第i个套餐的花费为Ci. 思路: 这道题比较容易超时.可能需要用到并查集的路径压缩,我下面的代码就是用了路径压缩,不然要超时.也是看了别人的代码才知道还有这种省时间的做法. 先介绍一下路径压缩吧: 如果并查集像一字长蛇这样排列的话,寻找起

最小生成树、次生成树、最短路劲、0-背包总结

1.最短路径 每组数据第一行是两个整数N.M(N<=100,M<=10000), N表示成都的大街上有几个路口, 标号为1的路口是商店所在地,标号为N的路口是赛场所在地, M则表示在成都有几条路. N=M=0表示输入结束. 接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路. 输入保证至少存在1条商店到赛场的路线. Output 对于每组输入,输出一行,表示工作人

POJ 3666 Making the Grade(数列变成非降序/非升序数组的最小代价,dp)

传送门: http://poj.org/problem?id=3666 Making the Grade Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9468   Accepted: 4406 Description A straight dirt road connects two fields on FJ's farm, but it changes elevation more than FJ would lik

考试又?叒叕出锅了

小迟修马路 road.in/.out/.cpp [问题描述] ?小迟生活的城市是1棵树(树指的是一个含有 n 个节点以及 n-1 条边的无向连通图),节点编号从 1 到 n,每条边拥有一个权值 value,表示通过这条边的时候你需要交纳的金钱(注意,有可能这个值为负数,也就是说你经过这条边的时候你可以赚钱)小迟是一个杰出的马路工,他居住在节点 s,他能够选择任意一个节点m,并将从节点 s 到节点 m 的简单路径(简单路径指的是不经过同一个节点两次)上的所有边的权值都修改为 0.现在小迟获得 q