• 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 }
    View Code
     别人写的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
    ");
    58         else printf("No
    ");
    59     }
    60     return 0;
    61 }
    View Code
    
    
    
     
  • 相关阅读:
    #sort 快速排序 20. 9.14
    #Trie Trie树调试模板 20.09.21
    #operator ——“Kruskal算法求最小生成树 中的 operator” ~20.8.17
    #STL #List 容器函数以及一些内置函数的用法
    刷题周记(三)——#最小生成树:Kruskal#二分图:染色法、匈牙利算法#拓扑#DFS:排列数字、n-皇后#BFS:走迷宫、八格码#List容器
    #周测 9 —— 高低位交换、Subsequences Summing to Sevens S、积木大赛、跳石头
    刷题周记(二)——KMP,Trie,最大异或对,(并查集)合并集合、连通块中点的数量、食物链,堆排序,单多源最短路、Dijkstra、bellman-ford、SPFA、Floyd、(堆优化)Prim
    4.SQL(数据库变更)
    3.SQL(查询)
    2.Oracle基本使用
  • 原文地址:https://www.cnblogs.com/crackpotisback/p/3857340.html
Copyright © 2020-2023  润新知