蓝桥杯:交换瓶子
有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
思路
这题一开始看懵了,还想穷举所有的交换组合,一看样例10000,肯定超时,后面发现:
- 因为序号是
1~n
,第i下标必定是数字i,那么只要不停地把正确的数字交换到正确的下标,就可以逐步完成排序 - 或者在
i~n
下标中选一个最小的换到i
下标,每次都这么做就可以完成排序,
证明:
方法1:改正一个错位置的元素,至少要交换一次,除非它本身就在应该的位置,否则没有比一次交换更少的方法了
方法2:已知一个序列的最小元素位置,把它放到第一个元素,即为正确的位置,必须进行一次交换,除非它本身就在应该的位置,否则没有比一次交换更少的方法了
代码
方法1:一直把元素放到正确的位置上
#include <iostream>
#include <algorithm>
using namespace std;
#define MAXLEN 10009
int a[MAXLEN];
int n;
int res = 0;
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>a[i];
}
for(int i=1; i<=n; i++)
{
while(a[i] != i)
{
swap(a[i], a[a[i]]);
res += 1;
}
}
cout<<res<<endl;
return 0;
}
方法2:选最小,然后放到第一个
#include <iostream>
#include <algorithm>
using namespace std;
#define MAXLEN 10009
int a[MAXLEN];
int n;
int res = 0;
int main()
{
cin>>n;
for(int i=1; i<=n; i++)
{
cin>>a[i];
}
for(int i=1; i<=n; i++)
{
int min = n+1;
int min_index = i;
for(int j=i; j<=n; j++)
{
if(a[j] < min)
{
min = a[j];
min_index = j;
}
}
if(min_index != i)
{
res += 1;
swap(a[min_index], a[i]);
}
}
cout<<res<<endl;
return 0;
}