zoukankan      html  css  js  c++  java
  • 51Nod 1515 明辨是非 —— 并查集 + 启发式合并

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1515

    题目来源: 原创
    基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题
     收藏
     关注

    给n组操作,每组操作形式为x y p。

    当p为1时,如果第x变量和第y个变量可以相等,则输出YES,并限制他们相等;否则输出NO,并忽略此次操作。

    当p为0时,如果第x变量和第y个变量可以不相等,则输出YES,并限制他们不相等 ;否则输出NO,并忽略此次操作。

    Input
    输入一个数n表示操作的次数(n<=1*10^5)
    接下来n行每行三个数x,y,p(x,y<=1*10^8,p=0 or 1)
    Output
    对于n行操作,分别输出n行YES或者NO
    Input示例
    3
    1 2 1
    1 3 1
    2 3 0
    Output示例
    YES
    YES
    NO

    题解:

    1.一开始还以为跟这题POJ2492 A Bug's Life一样,直接种类并查集即可,结果连测试数据都过不了。后来发现:当A!=B, B!=C时,A可能等于C,也可能不等于C,而对于POJ2492,因为元素只有两种,所以A肯定等于C。自己就是受这一题的影响,一直认为A肯定等于C,思想僵化……

    2.所以,同一个集合里的元素只能是相等的,即不能用种类并查集了。对此的解决策略是:为每个集合添加一个与之不相等集合的队列。

    3.当规定两个集合不相等时,只需各自把对方加入到自己的“不相等”队列即可;当规定两集合相等时,即需要合并两集合,此时,就需要用到启发式合并了:把“不相等”队列小的合并到“不相等”队列大的集合上。

    4.启发式合并的时间复杂度为:O(nlogn),证明:每一次把小集合合并到大集合上,则新集合的大小至少为小集合的两倍,即表明每合并一次,集合的大小可翻倍,由于只有n个元素,那么最多只能有logn次翻倍;每一次翻倍,最多只能有n个元素参与,所以时间复杂度为O(nlogn)。

    5.在检验两集合是否不相等时,由于还用到了find()函数,所以总体的时间复杂度为:O(n*logn*logn)

    代码如下:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <vector>
     6 #include <cmath>
     7 #include <queue>
     8 #include <stack>
     9 #include <map>
    10 #include <string>
    11 #include <set>
    12 using namespace std;
    13 typedef long long LL;
    14 const int INF = 2e9;
    15 const LL LNF = 9e18;
    16 const int MOD = 1e9+7;
    17 const int MAXN = 2e5+10;
    18 
    19 int fa[MAXN];
    20 map<int,int>M;  //用于离散化
    21 vector<int>s[MAXN];     //记录与这个变量不相等的变量
    22 int find(int x)
    23 {
    24     return fa[x]==-1?x:fa[x]=find(fa[x]);
    25 }
    26 
    27 bool Union(int u, int v, int w)
    28 {
    29     u = find(u);
    30     v = find(v);
    31 
    32     if(u==v)    //如果在同一个集合,即两变量相等,则直接判断
    33         return (w==0);
    34     else    //不在同一个集合
    35     {
    36         if(s[u].size()>s[v].size()) swap(u,v);
    37         bool flag = false;      //判断两变量是否不相等
    38         int sz = s[u].size();
    39         for(int i = 0; i<sz; i++)   //用小的集合去判断
    40         {
    41             flag |= find(s[u][i])==v;   //s[u][i]可能已经被合并到某个集合,所以要找到其当前所在的集合
    42             if(flag) break;     //加上这个判断,不然被卡常数
    43         }
    44 
    45         if(flag)    //不过两变量不相等,则直接判断,并返回
    46             return (w==1);
    47         else if(w==0)   //否则,如果要求两变量相等,则两者所在的集合
    48         {
    49             fa[u] = v;
    50             sz = s[u].size();
    51             for(int i = 0; i<sz; i++) //启发式合并的关键
    52                 s[v].push_back(s[u][i]);
    53             s[u].clear();
    54         }
    55         else    //如果要求两变量不相等,则各自把对方加入到自己的“不相等”队列
    56         {
    57             s[u].push_back(v);
    58             s[v].push_back(u);
    59         }
    60     }
    61     return true;
    62 }
    63 
    64 int main()
    65 {
    66     int n, m;
    67     while(scanf("%d",&n)!=EOF)
    68     {
    69         M.clear();
    70         m = 0;
    71         memset(fa,-1,sizeof(fa));
    72         for(int i = 1; i<MAXN; i++)
    73             s[i].clear();
    74         for(int i = 1; i<=n; i++)
    75         {
    76             int u,v,w;
    77             scanf("%d%d%d",&u,&v,&w);
    78             if(M.find(u)==M.end()) M[u] = ++m;
    79             if(M.find(v)==M.end()) M[v] = ++m;
    80 
    81             if(Union(M[u],M[v],!w)) puts("YES");
    82             else puts("NO");
    83         }
    84     }
    85 }
    View Code
  • 相关阅读:
    XP系统下快速切换ip的bat脚本配置
    Spring学习札记
    hibernate防止sql注入
    重载,继承,重写和多态的区别:
    Oracle Sql基础
    Android开发——利用Cursor+CursorAdapter实现界面实时更新
    Android开发——09Google I/O之让Android UI性能更高效(1)
    Android开发——MediaProvider源码分析(2)
    Android开发——Android搜索框架(二)
    [转]activity的启动方式(launch mode)
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8727716.html
Copyright © 2011-2022 走看看