交换瓶子问题
文章目录
前言
知道题目用暴力算法是可以过的,注意数据范围是1~10000,卡在一个微妙的地方,不免让人想用暴力算法,但是我们现在又不在赛场上,自然是多多益善,在这里还会介绍图论的解法,喜欢的小伙伴可以点个赞啦。
题目描述
有 N 个瓶子,编号 1∼N,放在架子上。
比如有 5 个瓶子:
2 1 3 5 4
要求每次拿起 2个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5
对于这么简单的情况,显然,至少需要交换 2 次就可以复位。
如果瓶子更多呢?你可以通过编程来解决。
输入格式
第一行包含一个整数 N,表示瓶子数量。
第二行包含 N 个整数,表示瓶子目前的排列状况。
输出格式
输出一个正整数,表示至少交换多少次,才能完成排序。
数据范围
1≤N≤10000,
输入样例1:
5
3 1 2 5 4
输出样例1:
3
输入样例2:
5
5 4 3 2 1
输出样例2:
2
暴力解法【能过】
类似于选择排序这样的,与当前位置不同的数直接在后面的数组当中找到符合的数与之交换,前面的数已经排好序了,不需要考虑
cpp
#include<iostream>
#include<algorithm>
using namespace std;
const int N =1e4+7;
int a[N];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int cnt=0;
for(int i=1;i<=n;i++){
if(a[i]!=i){
for(int j=i+1;j<=n;j++){
if(a[j]==i) swap(a[j],a[i]),cnt++;
}
}
}
cout<<cnt;
}
图论解法
知识预备【交换环】
我们先举例分析:看下图
我们可以得到 3 个环
每个环是闭环,入度为 1 ,出度为 1。构建方式如上图所示
下面我们对环进行操作:
假设有个大环:
每次分解只能产生一个环
而我们最终的目的是将所有大环分解成一个一个小环,每个瓶子回到了本该属于自己3的位置,每次的分解操作只是一次交换
swap(a[ i ] , a [ j ])
现在我们有 n 个数, 假设产生 k 个环 ,那么我们至少需要 n - k 步操作才能使得大环变成一个一个最小环。而只要有环的长度>=2
,那么我们就可以将其分解开来,使得可以在 n - k 步操作完成全部分解
现在我们只要求环的数量就行【类似于链表】 ,那么直接上代码
代码
解析都在代码注释当中呦
cpp
#include<iostream>
#include<algorithm>
using namespace std;
const int N =1e4 + 7;
bool st[N];//判断一个数是否有被纳入环内
int a[N];//存储原数组
int main(){
int n;cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
if(!st[i]){//从 1 号位开始枚举
ans++;
for(int j=i;!st[j];j=a[j]){
//这里j=a[j]是本来的数--》本来数的位置上 的 现在的数
st[j]=true;
}
}
}
cout<<n-ans<<endl;
}
暴力做法和图论做法的对比
上面的是图论,下面的是暴力。
总结
这边博客介绍了 2 种思路,图论的做法比较新奇,上述的结论也适合在其他类型的题目当中求解,后续还会更新图论相关的内容,喜欢的小伙伴可以点个关注啦