题目描述
给定一个数列 a,这个数列满足 ai ≠ aj,现在要求你把这个数列从小到大排序,每次允许你交换其中任意一对数,请问最少需要几次交换?
输入格式
第一行是一个整数,代表数字个数 n。
第二行有 n 个整数用空格分隔开,表示数列 a。
输出格式
只有一行,包含一个数,表示最少的交换次数。
输入输出样例
输入 #1复制
8 8 23 4 16 77 -5 53 100
输出 #1复制
5
说明/提示
数据规模与约定
对于100% 的数据,保证1<=n<=100000,ai在 int 内。
😁
解析:输入数据存入a[ ]中,再建立一个结构体b,存数值和下标位置,然后我们对b按照数值大小排序,然后我们遍历一波,如果a[ i ]≠b[ i ].n,那么就是要交换a[ i ]和a[ b[ i ].id ],但是注意,我们每次交换,交换的两个数对应的下标也就变化了,所以我们得在b中找到a[ i ],然后将其下标改成b[ b[i].id ],但是暴力遍历寻找会超时,所以我们得利用二分来解决。
#include <bits/stdc++.h>
using namespace std;
struct su
{
int id; //存放原始下标id
int n; //存放数值
bool operator<(const su&x)const{
return n<x.n;
}
}b[100005];
int a[100005];
int main()
{
int n,t,i,cnt=0,z,y,mid; //cnt记录需要交换的次数
scanf("%d",&n);
for(i=0;i<n;i++) scanf("%d",&a[i]),b[i].n=a[i],b[i].id=i; //输入a[]同时,完成对b的记录
sort(b,b+n); //对b排序
for(i=0;i<n;i++){
if(a[i]!=b[i].n){ //对应位置不一样,所以要交换
cnt++; //交换次数++
t=a[i];
a[i]=a[b[i].id];
a[b[i].id]=t; //交换
z=i+1,y=n-1; //因为换过去的肯定在>i的位置,所以二分左边界直接i+1开始
while(z<=y){
mid=(z+y)/2;
if(t==b[mid].n){
b[mid].id=b[i].id; //找到了,更新对应的id
break;
}else if(t<b[mid].n) y=mid-1;
else z=mid+1;
}
}
}
printf("%d\n",cnt);
return 0;
}