有 N 个瓶子,编号 1∼N,放在架子上。
比如有 55 个瓶子:
2 1 3 5 4
要求每次拿起 22 个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5
对于这么简单的情况,显然,至少需要交换 22 次就可以复位。
如果瓶子更多呢?你可以通过编程来解决。
输入格式
第一行包含一个整数 N,表示瓶子数量。
第二行包含 N个整数,表示瓶子目前的排列状况。
输出格式
输出一个正整数,表示至少交换多少次,才能完成排序。
数据范围
1≤N≤10000,
输入样例1:
5
3 1 2 5 4
输出样例1:
3
输入样例2:
5
5 4 3 2 1
输出样例2:
2
图析
意思就是假如我们能够编写代码算出样例按照这样的关系所能够构成的环的数量k,则每次交换任意两个瓶子会增加一个环,则至少交换n-k次就能够实现n个环,即像上面图中最后瓶子序号为 1 2 3 4 5各自成一个环。
代码(注释包含我对此题算法本质的一些理解,可以阅读一下)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=10010;
int n;
int b[N];
bool st[N];
int cnt;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
//从1开始的意义在于正好还原位置序号(数组下标)与瓶子序号的对应关系;
//位置 1 2 3 4 5
//瓶子 3 1 2 5 4 本质是位置序号(数组下标)与瓶子序号组成有向边;
//而st[]数组的意义则是记录位置序号所对应的瓶子是否已经被用来组成一个环;其数组下标也是位置序号-(从1开始)
for(int i=1;i<=n;i++) //i代表的是位置;不是瓶子的序号,这对我们理解程序的步骤至关重要;
{
if(st[i]==0)//表示这个位置的瓶子还没有被用来组成环;
{
cnt++; //环的数量加1;
for(int j=i;st[j]==0;j=b[j]) //遍历这个环所涉及的所有元素,j=b[j]->意味着遍历的
//顺序是当前位置对应的瓶子序号为下一个所要遍历的位置序号;
st[j]=true; //表示环里已经用过这个瓶子序号;
}
}
cout << n-cnt << endl;//n-k
return 0;
}
这就是我对此题应用的一些理解,记录一下方便之后翻阅,希望大家在评论区也能分享一下自己的心得,也指正一下我的错误,谢谢大家!