洛谷題單指南-集合-P1525 [NOIP2010 提高組] 關押罪犯

江城伍月發表於2024-03-22

原題連結:https://www.luogu.com.cn/problem/P1525

題意解讀:有很多罪犯,要關到兩座監獄,有一些罪犯之間有仇,並且可以量化出仇恨值,如果關在一起就會衝突,造成的影響就是仇恨值,要使得造成的影響最小,如果可以完全不起衝突,輸出0。

解題思路:

首先,要讓衝突影響最小化,顯然應該把仇恨大的罪犯分開。

將所有罪犯關係、仇恨值按仇恨值大小降序排序

遍歷每一對罪犯,判斷他們是否已經在同一個集合(監獄)

如果已經屬於同一個集合,則輸出他們的仇恨值,即為答案。

如果不屬於同一個集合,就要將兩人分到兩個集合(監獄),問題的關鍵來了,如何分配罪犯?

初始時,如果兩人之前都沒有仇人,則不著急合併,記錄下兩人為各自的仇人

接下來,如果兩人能找到各自仇人,則將其與對方的仇人放在一個集合。(解釋:a、b兩人,a如果已經有仇人了,說明a和仇人的仇恨值更大,因為是在前面遍歷到,a顯然不能和其仇人分到一個集合,應該將b跟a的仇人分到一個集合,同樣,a應該跟b的仇人分到一個集合)

100分程式碼:

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

const int N = 20005, M = 100005;

int p[N]; //並查集,罪犯所屬的集合

//查詢x所在集合
int find(int x)
{
    if(p[x] == x) return p[x];
    return p[x] = find(p[x]);
}
//將x、y合併
void merge(int x, int y)
{
    p[find(x)] = find(y);
}

struct node
{
    int a, b, c; //a 和 b的怨氣值c
} s[M];

bool cmp(node x, node y)
{
    return x.c >= y.c;
}

int enemy[N]; //儲存已出現的每個人的敵人-存在仇恨值的人
int n, m;

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= m; i++)
    {
        cin >> s[i].a >> s[i].b >> s[i].c;
    }

    for(int i = 1; i <= n; i++) p[i] = i;
    sort(s + 1, s + m + 1, cmp); //按仇恨值降序排序
    bool nowar = true; //是否沒有任何衝突
    for(int i = 1; i <= m; i++)
    {
        int u = s[i].a, v = s[i].b;
        if(find(u) == find(v)) //如果兩個人已經屬於同一個集合,則無法再劃分,此時的仇恨值即答案
        {
            cout << s[i].c;
            nowar = false; //說明會產生衝突
            break;
        }
        else
        {
            if(!enemy[u]) enemy[u] = v; //如果u沒有敵人,給u設定敵人v
            else merge(enemy[u], v); //如果u有敵人,把v和u的敵人分到一個集合
            if(!enemy[v]) enemy[v] = u; //如果v沒有敵人,給v設定敵人u
            else merge(enemy[v], u); //如果v有敵人,把u和v的敵人分到一個集合
        }
    }    
    if(nowar) cout << 0; //如果沒有任何衝突,輸出0

    return 0;
}

相關文章