Given any permutation of the numbers {0, 1, 2,..., N−1}, it is easy to sort them in increasing order. But what if Swap(0, *)
is the ONLY operation that is allowed to use? For example, to sort {4, 0, 2, 1, 3} we may apply the swap operations in the following way:
Swap(0, 1) => {4, 1, 2, 0, 3}
Swap(0, 3) => {4, 1, 2, 3, 0}
Swap(0, 4) => {0, 1, 2, 3, 4}
Now you are asked to find the minimum number of swaps need to sort the given permutation of the first N nonnegative integers.
Input Specification:
Each input file contains one test case, which gives a positive N (≤105) followed by a permutation sequence of {0, 1, ..., N−1}. All the numbers in a line are separated by a space.
Output Specification:
For each case, simply print in a line the minimum number of swaps need to sort the given permutation.
Sample Input:
10
3 5 7 2 6 4 9 0 8 1
Sample Output:
9
题目大意:
给一组无序的数,通过多次的swap(0,*)变换,将该组数变为递增序列。
思路分析:
首先约定,若数字 i 位于下标为 i 的位置上,则说 i 位于正确的位置;
经过尝试,最终分为两种情况:
① 0不在正确的位置上,则0与0所在位置的数字交换,即swap(a[ 0所在的位置],a[ 0所在的位置的数字所在的位置]);
② 0在正确的位置上,则0与任一未处于正确位置的数字交换。
基于以上两种结论,我写出了第一版暴力的代码,但是超时了,然后就是基于以上两个结论的时间优化。
① 0的位置可以在输入的时候标记,然后随着每次交换的进行,不断记录;另一个下标则使用 map<数字,该数字所在位置的下标>,可以直接索引到某一数字所在的位置。
② 这个结论要重点优化找到未处于正确位置的数字的过程,如果挨个遍历的话也会超时,可以使用set记录当前位置上数字不正确的所有位置,每次需要的时候就调用set的第一个数。
优化后的代码如下:
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
#define maxn 100000+10
using namespace std;
int a[maxn];
map<int,int>m;
set<int>s;
int main()
{
int n;cin>>n;
int ans=0;
int k;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
m[a[i]]=i;
if(a[i]==0)k=i;
if(a[i]!=i&&i!=0){
s.insert(i);
}
}
while(1){
if(k!=0){
swap(a[m[k]],a[k]);
ans++;
s.erase(s.find(k));
int t=m[k];m[k]=k;k=t;
}
else {
if(s.empty())break;
else{
set<int>::iterator it=s.begin();
swap(a[*it],a[0]);
ans++;
m[a[0]]=0;
m[0]=*it;
k=*it;
}
}
}
cout<<ans<<endl;
return 0;
}