看了这道题的题解,都是用 dp 写的,在这里献上一篇贪心做法。
贪心做法
基于拓扑排序的思想,可以先把所有入度为 0 0 0 的点放入队列中。
对于队列中的的每一个点:
(记这个点为 t t t,下个点为 a [ t ] a[t] a[t])
- 试图将 t t t 其变为“上帝手中的点”,则 a [ t ] a[t] a[t] 将被放入星球,所以使 a n s + 1 ans+1 ans+1。
- 标记 t t t 和 a [ t ] a[t] a[t] 已经遍历过了。
- 将 a [ a [ t ] ] a[a[t]] a[a[t]] 的入度 − 1 -1 −1,若它的入度被减为 0 0 0,并且没有被遍历过,表明它能成为新的“上帝手中的点”,将它加入队列。
现在,能被我们贪的点已经全部贪完了,剩下的点构成了若干个环。而对于每一个环,其中每两个点可以能再放一个进入星球。
所以先求出每个环的大小,再将换的大小乘
2
2
2 统计入答案即可
AC 代码
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 1145141;
// 快读
void read(int &x) {
x = 0;
char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - 48;
ch = getchar();
}
}
int tmp[100];
// 快写
void write(int x) {
int cnt = 0;
while (x > 0) {
tmp[++cnt] = x % 10;
x /= 10;
}
while (cnt > 0) {
putchar(tmp[cnt] + 48);
--cnt;
}
putchar('\n');
}
int n, a[N], b[N], ans = 0;
bool vis[N];
queue<int> q;
void bfs() {
int t;
while (!q.empty()) {
t = q.front();
q.pop();
vis[t] = true;
if (!vis[a[t]]) {
ans++;
vis[a[t]] = true;
if (--b[a[a[t]]] == 0 && !vis[a[a[t]]]) {
q.push(a[a[t]]);
}
}
}
}
int main() {
read(n);
for (int i = 1; i <= n; ++i) {
read(a[i]);
b[a[i]]++;
}
for (int i = 1; i <= n; ++i) {
if (b[i] == 0) q.push(i);
}
//先贪
bfs();
//统计剩下的每个环的大小
int k, cnt;
for (int i = 1; i <= n; ++i) {
if (!vis[i]) {
k = i;
vis[k] = true;
cnt = 1;
while (!vis[a[k]]) {
k = a[k];
vis[k] = true;
cnt++;
}
ans += cnt >> 1;
}
}
write(ans);
return 0;
}
1645

被折叠的 条评论
为什么被折叠?



