HDU1811并查集+拓扑排序

本文介绍了一种基于Rating和RP的Tetris排名系统实现方法。通过使用拓扑排序和并查集处理玩家间的相对等级关系,确保了排名系统的准确性和一致性。文章详细解析了解决方案中的关键步骤和技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

Rank of Tetris

自从Lele开发了Rating系统,他的Tetris事业更是如虎添翼,不久他遍把这个游戏推向了全球。

为了更好的符合那些爱好者的喜好,Lele又想了一个新点子:他将制作一个全球Tetris高手排行榜,定时更新,名堂要比福布斯富豪榜还响。关于如何排名,这个不用说都知道是根据Rating从高到低来排,如果两个人具有相同的Rating,那就按这几个人的RP从高到低来排。

终于,Lele要开始行动了,对N个人进行排名。为了方便起见,每个人都已经被编号,分别从0到N-1,并且编号越大,RP就越高。
同时Lele从狗仔队里取得一些(M个)关于Rating的信息。这些信息可能有三种情况,分别是"A > B","A = B","A < B",分别表示A的Rating高于B,等于B,小于B。

现在Lele并不是让你来帮他制作这个高手榜,他只是想知道,根据这些信息是否能够确定出这个高手榜,是的话就输出"OK"。否则就请你判断出错的原因,到底是因为信息不完全(输出"UNCERTAIN"),还是因为这些信息中包含冲突(输出"CONFLICT")。
注意,如果信息中同时包含冲突且信息不完全,就输出"CONFLICT"

解题思路

偏序求全序,比较常见的有Floyd和拓扑排序,看数据范围显然是拓扑排序。根据><建有向边,进行拓扑排序,如果某一时刻队列中有两个元素,说明两个人的rank相同,也就是"UNCERTAIN"。

但是题目中加入了=,还需要判断"CONFLICT"。如果没有=,冲突的情况只有A>B && B>A,也就是成环的情况。有了=,多了一种冲突情况,也就是A=B && A>C && C>B
我们可以用并查集将两个元素合并成一个元素。这样就把等于的情况又转换成了上面成环的情况。A=B && A>C && C>B ===》 A=B=X && X>C && C>X

拓扑排序中,是否所有元素的rank确定就是判断是否成环的条件,

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 10010, M = 20010;
int n, m;
struct node
{
    int v, next;
} edge[M * 2];
int head[N], ct = 1;
int degree[N];
int vis[N];
int fa[N];

void add(int u, int v)
{
    edge[ct].v = v;
    edge[ct].next = head[u];
    head[u] = ct++;
    degree[v]++;
}

set<int> st;
int topo(int ned)
{
    queue<int> q;
    int ans1 = 0, ans2 = 0; //信息不全 、 冲突
    for (int i = 1; i <= n; i++)
    {
        if (degree[i] == 0 && st.count(i))
        {
            vis[i] = 1;
            q.push(i);
        }
    }
    if (q.size() >= 2)
        ans1 = 1;
    while (q.size())
    {
        int pos = q.front();
        q.pop();
        for (int i = head[pos]; i; i = edge[i].next)
        {
            int v = edge[i].v;
            degree[v]--;
            if (degree[v] == 0 && !vis[v] && st.count(v))
                q.push(v), vis[v] = 1;
        }
        if (q.size() >= 2)
            ans1 = 1;
    }
    int res = 0;
    for (int i = 1; i <= n; i++)
        if (vis[i])
            res++;
    if (res < ned)
        ans2 = 1;
    if (ans2)
        return 2;
    else if (ans1)
        return 0;
    return 1;
}

int a[20010], b[20010];
char c[20010];

int find(int x)
{
    if (x == fa[x])
        return x;
    return fa[x] = find(fa[x]);
}
void mix(int a, int b)
{
    int ffa = find(a);
    int ffb = find(b);
    fa[ffa] = ffb;
}
void solve()
{
    st.clear();
    ct = 1;
    for (int i = 1; i <= n; i++)
        head[i] = degree[i] = vis[i] = 0, fa[i] = i;

    for (int i = 1; i <= m; i++)
    {
        scanf("%d %c %d", &a[i], &c[i], &b[i]);
        ++a[i], ++b[i];
        if (c[i] == '=')
            mix(a[i], b[i]);
    }
    for (int i = 1; i <= n; i++)
    {
        int ii = find(i);
        st.insert(ii);
    }

    for (int i = 1; i <= m; i++)
    {
        a[i] = find(a[i]), b[i] = find(b[i]);
        if (c[i] == '=')
            continue;
        if (c[i] == '<')
            swap(a[i], b[i]);
        add(a[i], b[i]);
    }
    int opt = topo(st.size());

    if (opt == 1)
        puts("OK");
    else if (opt == 2)
        puts("CONFLICT");
    else if (opt == 0)
        puts("UNCERTAIN");
}
int main()
{
    while (cin >> n >> m)
        solve();
    return 0;
}

### HDU 3342 并查集 解题思路与实现 #### 题目背景介绍 HDU 3342 是一道涉及并查集的数据结构题目。该类问题通常用于处理动态连通性查询,即判断若干元素是否属于同一集合,并支持高效的合并操作。 #### 数据描述 给定一系列的人际关系网络中的朋友关系对 (A, B),表示 A 和 B 是直接的朋友。目标是通过这些已知的关系推断出所有人之间的间接友谊连接情况。具体来说,如果存在一条路径使得两个人可以通过中间人的链条相连,则认为他们是间接朋友。 #### 思路分析 为了高效解决此类问题,可以采用带按秩压缩启发式的加权快速联合-查找算法(Weighted Quick Union with Path Compression)。这种方法不仅能够有效地管理大规模数据集下的分组信息,而且可以在几乎常数时间内完成每次查找和联合操作[^1]。 当遇到一个新的友链 `(a,b)` 时: - 如果 a 和 b 已经在同一棵树下,则无需任何动作; - 否则,执行一次 `union` 操作来把它们所在的两棵不同的树合并成一棵更大的树; 最终目的是统计有多少个独立的“朋友圈”,也就是森林里的树木数量减一即是所需新建桥梁的数量[^4]。 #### 实现细节 以下是 Python 版本的具体实现方式: ```python class DisjointSet: def __init__(self, n): self.parent = list(range(n)) self.rank = [0] * n def find(self, p): if self.parent[p] != p: self.parent[p] = self.find(self.parent[p]) # 路径压缩 return self.parent[p] def union(self, p, q): rootP = self.find(p) rootQ = self.find(q) if rootP == rootQ: return # 按秩合并 if self.rank[rootP] > self.rank[rootQ]: self.parent[rootQ] = rootP elif self.rank[rootP] < self.rank[rootQ]: self.parent[rootP] = rootQ else: self.parent[rootQ] = rootP self.rank[rootP] += 1 def solve(): N, M = map(int, input().split()) dsu = DisjointSet(N+1) # 初始化不相交集 for _ in range(M): u, v = map(int, input().split()) dsu.union(u,v) groups = set() for i in range(1,N+1): groups.add(dsu.find(i)) bridges_needed = len(groups)-1 print(f"Bridges needed to connect all components: {bridges_needed}") solve() ``` 这段代码定义了一个名为 `DisjointSet` 的类来进行并查集的操作,包括初始化、寻找根节点以及联合两个子集的功能。最后,在主函数 `solve()` 中读取输入参数并对每一对好友调用 `dsu.union()` 方法直到遍历完所有的边为止。之后计算不同组件的数量从而得出所需的桥接次数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hesorchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值