洛谷P2835 刻录光盘 题解 点的度数+并查集
题目链接:https://www.luogu.com.cn/problem/P2835
解题思路:
如果A愿意借给B,则B的入度加一。
然后计算一下有多少个点的入度为0,这些是我们需要分配光盘的。
但是可能存在强联通分量,这种情况下,一个强联通分量里没有入读为0的点,但是我需要制定以下这个点。
所以我用 并查集 来维护,如果A愿意借给B,则A、B属于同一个集合。
首先计算有多少入度为0的点,假设有 \(cnt_1\) 个,找到这些点的同时将它们所属的集合标记为访问过。
然后计算有多少个没有被标记过的集合,假设这个数量为 \(cnt_2\)。
则最终答案为: \(cnt_1 + cnt_2\)。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 220;
int n, d[maxn], f[maxn], a, cnt;
bool vis[maxn];
void init() {
for (int i = 1; i <= n; i ++) f[i] = i;
}
int Find(int x) {
return x == f[x] ? x : f[x] = Find(f[x]);
}
void Union(int x, int y) {
int a = Find(x), b = Find(y);
f[a] = f[b] = f[x] = f[y] = min(a, b);
}
int main() {
cin >> n;
init();
for (int i = 1; i <= n; i ++) {
while ((cin >> a) && a) {
d[a] ++;
Union(i, a);
}
}
for (int i = 1; i <= n; i ++) if (!d[i]) { vis[Find(i)] = true; cnt ++; }
for (int i = 1; i <= n; i ++) if (!vis[Find(i)]) { vis[Find(i)] = true; cnt ++; }
cout << cnt << endl;
return 0;
}