产生冠军 HDU - 2094(连通/树/思维)

题意:有一群人,打乒乓球比赛,两两捉对撕杀,每两个人之间最多打一场比赛。
球赛的规则如下:
如果A打败了B,B又打败了C,而A与C之间没有进行过比赛,那么就认定,A一定能打败C。
如果A打败了B,B又打败了C,而且,C又打败了A,那么A、B、C三者都不可能成为冠军。
根据这个规则,无需循环较量,或许就能确定冠军。你的任务就是面对一群比赛选手,在经过了若干场撕杀之后,确定是否已经实际上产生了冠军。

分析:以下两种思路。

  1. 连通 + 入度为 0 的点只有一个:就可以确定冠军;否则不可以。
  2. 思维:确定冠军,那么依据胜利关系可以建立一棵树,根节点作为冠军。
    按照树的节点数与边数的关系:n = m+1,
    也就是总人数 = 失败的人数 + 1.
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2010, INF = 0x3f3f3f3f;
int n, m,d[N];
string a, b;
map<string, int> hs;
map<int, string> rhs;
vector<int> g[N];
bool st[N];

bool bfs() {
    memset(st, 0, sizeof(st));
    queue<string> q;
    for (auto u : hs)
        if (d[u.second] == 0) 
            q.push(u.first);
    if (q.size() > 1) return 0;
    int cnt = 0;
    while (q.size()) {
        string u = q.front(); q.pop();
        cnt++;
        for (auto v : g[hs[u]]) {
            if (!st[v])
                q.push(rhs[v]), st[v] = 1;
        }
    }
    return cnt == m;
}
int main() {
    while (cin >> n && n) {
        for (int i = 1; i <= n; i++) {
            cin >> a >> b;
            if (!hs.count(a)) hs[a] = ++m, rhs[m] = a;
            if (!hs.count(b)) hs[b] = ++m, rhs[m] = b;
            g[hs[a]].push_back(hs[b]), d[hs[b]]++;
        }
        if (bfs()) cout << "Yes\n";
        else cout << "No\n";

        for (int i = 0; i <= m; i++) g[i].clear(), d[i] = 0;
        hs.clear(), rhs.clear(), m = 0;
    }
    return 0;
}

int main2() {
    while (cin >> n && n) {
        set<string> s1, s2;
        for (int i = 1; i <= n; i++) {
            cin >> a >> b;
            s1.insert(a), s1.insert(b);
            s2.insert(b);
        }
        if (s1.size() == s2.size() + 1) cout << "Yes\n";
        else cout << "No\n";
    }
    return 0;
}
posted @ 2023-03-29 22:06  HelloHeBin  阅读(50)  评论(0编辑  收藏  举报