题目:
有N个瓶子,编号 1 ~ N,放在架子上。
比如有5个瓶子:
2 1 3 5 4
要求每次拿起2个瓶子,交换它们的位置。
经过若干次后,使得瓶子的序号为:
1 2 3 4 5
对于这么简单的情况,显然,至少需要交换2次就可以复位。
如果瓶子更多呢?你可以通过编程来解决。
输入格式为两行:
第一行: 一个正整数N(N<10000), 表示瓶子的数目
第二行:N个正整数,用空格分开,表示瓶子目前的排列情况。
输出数据为一行一个正整数,表示至少交换多少次,才能完成排序。
例如,输入:
5
3 1 2 5 4
程序应该输出:
3
再例如,输入:
5
5 4 3 2 1
程序应该输出:
2
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
思路:
转化成图论的问题,数组的每个位置都可以看成是一个环(因为每一个位置必然指向一个位置,且有一个位置指向它)我们想把环的数量变成n个,也就是每一个位置的“指向”都指向自己。
那么对于任意一个环,交换他们中的 任意两个“指向” 的位置,其产生的影响必然是裂成两个环。而交换不同环 中的 任意两个“指向”的位置,其产生的影响必然是合并两个环。
此题既可以转化成——“求输入数组构成环的数量k”,每次交换操作至多可以使环的数量+1,所以,最少交换次数就是 ——n-k;
那环的数量如何求呢————
(哈哈哈,就皮一下~别打别打)其实很简单啦,可以开一个bool数组来记录每个位置是否遍历过。如果找到了一个没有遍历过的位置就说明找到了一个新的环,cnt++,并且循环这个环并且标记所有位置,直到回到头部的位置。
for(int i=1;i<=n;++i){
if(!st[i]){
cnt++;
for(int j=i;!st[j];j=a[j])st[j]=true;
}
核心代码就是这些啦,这道题其实总共代码就十行左右的样子。这个解法是不是比较巧妙,博主是没有系统学习图论的,但是也可以听得懂这个方法,我们可以一起学习一下。
加油~后天就是蓝桥杯省赛啦,预祝各位备考蓝桥杯的同学们都取得理想的成绩~
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e4+5;
int a[N];
bool st[N];
int main(){
int n; cin >> n;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
int cnt=0;
for(int i=1;i<=n;++i){
if(!st[i]){
cnt++;
for(int j=i;!st[j];j=a[j])st[j]=true;
}
}
cout<<n-cnt;
return 0;
}
种一棵树的最好时间是十年前,其次是现在~