HDU 1811(并查集+拓扑排序)

其实好久都没写题解了  本来也不想写这篇题解的 但是上网一看所有题解长得一模一样 也没有什么具体分析(毕竟天下题解一大抄嘛 233333) 还是打算写写这个题的分析吧

具体分析:

   如果A > B 那么我们连一条A到B的边, 反之连B到A的边;

    首先想想 如果答案成为ok 那么这些关系最终应该组成一条链才是 如果有环那么结果应该冲突 如果是一颗树 那么两颗子树的相对关系没法确定 所以最后的图一定是一条链才能满足所有关系

    但是这题有一个等于关系有点麻烦 这时候我们可以用并查集缩点 例如A = B, B = C, C = D, 那么可以把A, B, C, D视为一个点, (都长得一样自然可以视为一体)最后求一遍拓扑序  如果是一条链 那么这个关系我们是能确定的 如果有环 那么关系冲突 如果是树 那就是信息不足

#include <bits/stdc++.h>
#define F first
#define S second
#define PLL pair<ll, ll>
#define PII pair<int, int>
#define PDD pair<double, double>
#define met(a, b) memset(a, b, sizeof(a))
#define FAST ios::sync_with_stdio(false)
typedef long long ll;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int MOD = 1e9 + 7;
const int N = 1e4 + 10, M = N * 2;
using namespace std;

struct EdgeNode {
    int to, next;
} edges[M];

int idx, h[N];
int a[N], b[N], F[N];
char op[N];
int ret, que[N], din[N];

inline void add(int u, int v) {
    edges[idx] = {v, h[u]}, h[u] = idx++;
    return ;
}

inline int Find(int x) {
    if (F[x] != x) F[x] = Find(F[x]);
    return F[x];
}

inline void Union(int x, int y) {
    F[Find(y)] = Find(x);
    return ;
}

void Topsort(int n) {
    bool flag = true;
    int hh = 0, tt = -1;
    for (int i = 0; i < n; i++) {
        if (!din[i] && Find(i) == i) que[++tt] = i;
    }

    while (hh <= tt) {
        if (tt - hh > 0) flag = false; //如果队列里数量大于一 那么就形成一棵树
        int q = que[hh++];
        ret--;

        for (int i = h[q]; ~i; i = edges[i].next) {
            int to = edges[i].to;
            if (--din[to] == 0) que[++tt] = to;
        }
    }

    if (ret > 0) printf("CONFLICT\n"); //如果ret > 0 那么就形成一个环 至于为什么 读者可以自己思考一下
    else if (!flag) printf("UNCERTAIN\n");
    else printf("OK\n");
    return ;
}

void init(int n) {
    idx = 0, ret = n;
    met(h, -1), met(din, 0);
    for (int i = 0; i < N; i++) F[i] = i;
    return ;
}

int main() {
    FAST;
    int n, m;
    while (~scanf("%d %d", &n, &m)) {
        init(n);

        for (int i = 0; i < m; i++) { //缩点 ret记录缩点后剩余点的数量
            scanf("%d %c %d", &a[i], &op[i], &b[i]);
            if (op[i] == '=' && Find(a[i]) != Find(b[i])) Union(a[i], b[i]), ret--;
        }

        for (int i = 0; i < m; i++) {
            int u = Find(a[i]), v = Find(b[i]);
            if (op[i] == '>') add(u, v), din[v]++;
            else if (op[i] == '<') add(v, u), din[u]++;
        }
        Topsort(n);
    }
    return 0;
}

/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
             .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         The program have no BUG.
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值