• uvalive 4960 Sensor Network


    题意:

    给出一个无向图,求一个生成树使得这个生成树的最大边与最小边之差最小,输出这个最小的差值。n的最大值为350。

    思路:

    这题不看题解想破头也不知道怎么写Orz。

    暴力的做法是可以从大到小枚举边作为最小边的权值,求MST,但是复杂度达到了O(n^4),很显然会T。

    考虑在kruskal算法加边的时候,当两个点在同一个连通分量的时候,加入这条边会形成环,这个时候就把环中的最小边去掉,剩下的边就尽可能达到了最大,当前加入的边设为Ea也最大的,然后再找现在生成树中的最小边Eb;当有n-1条边的时候,就形成了一个生成树,就可以利用Ea – Eb去更新答案。

    最重要的两个过程是找环和加边。

    判断环,用了LCA的思想,但并不是倍增,而是纯粹暴力的dfs。对于一条边的两个点假设为x和y,首先从一个点x开始访问,直到访问到它的祖先,即满足条件par[x] == x,标记这个过程中的所有点;然后从y开始访问,直到访问到它的祖先(同上)或者它自己或某一个祖先被标记了为止,若y访问到祖先都没有点被标记,那么就说明x和y没有公共祖先,那么它们就不在同一个连通分量中,加入这条边就不会形成环。

    若找到了环,那么就要求这个环中边的最小值,求法很自然,遍历从x出发到lca的所有边,遍历从y出发到lca的所有边,找出其中的最小边,之后去掉这条边,去掉这条边有个技巧,假设最小边的非父亲点为u,那么去掉这条边时,令par[u] = u,这条边就被去掉了。之后再找最小边即可。

    之后就是加边的过程。并查集加边是启发式合并,但是这题的加边是有方向的加边

    假设两个点x和y,如果有其中一个点就是祖先,那么就把这个点连到另外一个点上(如x为祖先点,那么令par[x] = y)。

    但是如果两个点都不是祖先,那么就要考虑如何合并,因为一个点不可能同时有两个父亲

    可以考虑从一个点开始访问,直到访问到祖先,将路径上的点全部按访问的顺序记录在一个vector中,之后逆向加边。

    再把这个点连到另一个点上。如图:

    合并前:

    合并后:

    最后再把这个点(从这个点开始访问的)连接到另一个点上,加边就完成了。

    枚举边的复杂度为O(n^2),找环和加边的复杂度为O(n),所以总的复杂度为O(n^3)。

    感谢MZjj帮我debug!

    代码:

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <iostream>
      4 #include <algorithm>
      5 #include <vector>
      6 using namespace std;
      7 
      8 const int N = 400;
      9 const int inf = 0x3f3f3f3f;
     10 
     11 struct edge
     12 {
     13     int x,y,w;
     14     
     15     edge(){};
     16     
     17     edge(int a,int b,int c)
     18     {
     19         x = a;
     20         y = b;
     21         w = c;
     22     }
     23 };
     24 
     25 vector<edge> es;
     26 
     27 int par[N];
     28 int n,m;
     29 int dis[N][N];
     30 bool vis[N];
     31 int num;
     32 int Minedge;
     33 struct edge minedge;
     34 
     35 bool cmp(edge aa,edge bb)
     36 {
     37     return aa.w < bb.w;
     38 }
     39 
     40 void init(void)
     41 {
     42     for (int i = 0;i < n;i++) par[i] = i;
     43     
     44     num = 0;
     45     
     46     es.clear();
     47     
     48     memset(dis,inf,sizeof(dis));
     49     
     50     Minedge = inf;
     51 }
     52 
     53 int LCA(int x,int y)
     54 {
     55     memset(vis,0,sizeof(vis));
     56     
     57     while (1)
     58     {
     59         vis[x] = 1;
     60         
     61         if (x == par[x]) break;
     62         
     63         x = par[x];
     64     }
     65     
     66     while (!vis[y] && y != par[y])
     67     {
     68         y = par[y];
     69     }
     70     
     71     if (!vis[y]) return -1;
     72     
     73     return y;
     74 }
     75 
     76 void findcycle(int k)
     77 {
     78     int x = es[k].x,y = es[k].y;
     79     
     80     int lca = LCA(x,y);
     81     
     82     if (lca == -1) return;
     83     
     84     minedge.w = inf;
     85     
     86     while (x != par[x] && x != lca)
     87     {
     88         if (dis[x][par[x]] < minedge.w)
     89         {
     90             minedge = edge(par[x],x,dis[x][par[x]]);
     91         }
     92         
     93         x = par[x];
     94     }
     95     
     96     while (y != par[y] && y != lca)
     97     {
     98         if (dis[y][par[y]] < minedge.w)
     99         {
    100             minedge = edge(par[y],y,dis[y][par[y]]);
    101         }
    102         
    103         y = par[y];
    104     }
    105     
    106     par[minedge.y] = minedge.y;
    107     
    108     Minedge = inf;
    109     
    110     for (int i = 0;i < n;i++)
    111     {
    112         Minedge = min(dis[par[i]][i],Minedge);
    113     }
    114     
    115     num--;
    116 }
    117 
    118 void adde(int k)
    119 {
    120     int x = es[k].x,y = es[k].y;
    121     
    122     if (x == par[x]) par[x] = y;
    123     else if (y == par[y]) par[y] = x;
    124     else
    125     {
    126         vector<int> v;
    127         
    128         while (1)
    129         {
    130             v.push_back(x);
    131             
    132             if (x == par[x]) break;
    133             
    134             x = par[x];
    135         }
    136         
    137         for (int i = v.size() - 1;i > 0;i--) par[v[i]] = v[i-1];
    138         
    139         par[es[k].x] = es[k].y;
    140     }
    141     
    142     num++;
    143     
    144     Minedge = min(Minedge,es[k].w);
    145 }
    146 
    147 int main()
    148 {
    149     while (scanf("%d",&n) && n)
    150     {
    151         scanf("%d",&m);
    152         
    153         init();
    154         
    155         for (int i = 0;i < m;i++)
    156         {
    157             int a,b,c;
    158             
    159             scanf("%d%d%d",&a,&b,&c);
    160             
    161             es.push_back(edge(a,b,c));
    162         }
    163         
    164         for (int i = 0;i < m;i++)
    165         {
    166             int x = es[i].x,y = es[i].y;
    167             
    168             dis[x][y] = dis[y][x] = es[i].w;
    169         }
    170         
    171         sort(es.begin(),es.end(),cmp);
    172         
    173         int ans = inf;
    174         
    175         for (int i = 0;i < m;i++)
    176         {
    177             findcycle(i);
    178             adde(i);
    179             
    180             if (num == n - 1) 
    181             {
    182                 //cout << es[i].w << " ** " << Minedge << endl;
    183                 ans = min(ans,es[i].w - Minedge);
    184             }
    185         }
    186         
    187         cout << ans << endl;
    188     }
    189     
    190     return 0;
    191 }
  • 相关阅读:
    重启sqlserver服务命令
    k8s学习
    collection包1.1.0都升级了什么功能
    Golang项目的测试实践
    一个让业务开发效率提高10倍的golang库
    GopherChina第二天小结
    GopherChina第一天小结
    slice是什么时候决定要扩张?
    史上最快的后台搭建框架
    gorm的日志模块源码解析
  • 原文地址:https://www.cnblogs.com/kickit/p/8808886.html
Copyright © 2020-2023  润新知