1224-交换瓶子
题目描述
关系
内容
问题分析
最初思路
如果一个数,不在其对应的坑位,那么是一定要进行交换的。找谁交换呢?假设数 a 占据了错误的坑位 i,数 b 占据了错误的坑位 j,如果 a 的正确坑位(排序后的位置)为 j,b 的正确坑位为 i,那么 a 和应该交换。
所以,思路是保留原数组,然后在进行排序。
思路分析
尝试以图论的角度来分析,把每个瓶子看成一个点。每个瓶子向它应该在的位置的瓶子结点连一条边。如下图:

- 3原本的位置是3,而3位置被2号瓶子占有,所以3号瓶子应该连接2号瓶子。
- 我们会发现,所有点的出度和入度都为1,所有图都是由数个环组成的;
- 我们最终期望的样子是变成5个环;
对于交换有两种情况:
- 交换同一个环中的两个点,会裂变成两个独立的环;
- 交换不同环中的两个点,会合并两个环;
假设一开始,我们构造出了 n 个环,最后我们需要裂变成 k 个环,那么只需要 n - k 次操作就可以实现了。
执行流程设计
暴力
- 备份原数组;
- 对 backup 进行排序;
- 遍历原数组,如果发现有一个数不在其位置上,就在排序后的数组中找到对应的位置。(通过这个数,找到本来的位置,本来的位置就是这个数的值
a[i],再通过这个位置 i 遍历 a 数组找到应该交换的数b[i]);
图论
- 构造环,得出一开始环的个数 cnt;
- 最后的答案就为 n - cnt;
总结
代码实现
图论
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n, cnt;
bool st[N];
int a[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
if (st[i]) continue;
cnt++;
for (int j = i; !st[j]; j = a[j]) {
st[j] = true;
}
}
printf("%d", n - cnt);
return 0;
}