大意: 只允许交换相邻两个数的情况下,问最少需要多少步完成排序(升序)操作?
分析: 模拟,推理后发现,对于每个逆序对都需要1次交换操作。而且可以证明:最优情况下也只需要交换逆序对。
简单说明一下:
①每个逆序对必须至少对应一次交换操作。
② 若当前没有逆序对,说明已经排好序了。
③选择两个数A , B交换, 若A,B为顺序,交换后产生了新的逆序对,需要的操作数至少加一;若A,B为逆序,则逆序对数量减一,一步步趋向升序。
问题就是求逆序对数,这里写了三种实现方法(效率:并归 > 树状数组 > 线段树):
线段树:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=500050;
#define Lson (cur)<<1,L,mid
#define Rson (cur)<<1|1,mid+1,R
typedef pair<int,int>PII;
int T[maxn],Lisan[maxn],N;
PII A[maxn];
LL COUNT;
int tree[maxn<<2];
void update(int cur){
tree[cur] = tree[cur<<1] + tree[cur<<1|1];
}
void Insert(int cur,int L,int R,int P){
if(L==P && R==P){
tree[cur]++; return ;
}
int mid=(L+R)/2;
if(P<=mid) Insert(Lson,P);
else Insert(Rson,P);
update(cur);
}
int Query(int cur,int L,int R,int l,int r){
if(l<=L && R<=r){
return tree[cur];
}
int mid=(L+R)/2,ans=0;
if(l<=mid) ans += Query(Lson,l,r);
if(r>mid) ans += Query(Rson,l,r);
return ans;
}
int main()
{
while(~scanf("%d",&N) && N){
for(int i=1;i<=N;i++){
scanf("%d",&A[i].first); A[i].second=i;
}
sort(A+1,A+1+N);
for(int i=1;i<=N;i++)
{
Lisan[A[i].second]=i;
}
memset(tree,0,sizeof(tree));
LL ans=0;
for(int i=1;i<=N;i++){
int p=Lisan[i];
ans += Query(1,1,N+1,p+1,N+1);
Insert(1,1,N+1,p);
}
printf("%I64d\n",ans);
}
return 0;
}
树状数组:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef pair<int,int>PII;
const int maxn=500050;
int Lisan[maxn],C[maxn],N;
PII A[maxn];
int lowbit(int x){
return x&(-x);
}
void Insert(int i,int d){
while(i<=N){
C[i] += d;
i += lowbit(i);
}
}
int Query(int i){
int ans=0;
while(i>0){
ans += C[i];
i -= lowbit(i);
}
return ans;
}
int main()
{
while(~scanf("%d",&N) && N){
memset(C,0,sizeof(C));
for(int i=1;i<=N;i++){
scanf("%d",&A[i].first); A[i].second=i;
}
sort(A+1,A+N+1);
for(int i=1;i<=N;i++){
Lisan[A[i].second]=i;
}
LL ans=0;
for(int i=1;i<=N;i++){
Insert(Lisan[i],1);
ans += Query(N)-Query(Lisan[i]);
}
printf("%I64d\n",ans);
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=500050;
int A[maxn],T[maxn],N;
LL COUNT;
void Merge_sort(int *A,int x,int y,int *T){
if(y-x <= 1) return ;
int m=x+(y-x)/2,p=x,q=m,i=x;
Merge_sort(A,x,m,T);
Merge_sort(A,m,y,T);
while(p < m || q < y){
if(q >= y || (p < m && A[p]<=A[q])) {
T[i++]=A[p++];
}
else {
T[i++]=A[q++]; COUNT += m-p;
}
}
for(i=x; i<y; i++) A[i]=T[i];
}
int main()
{
while(~scanf("%d",&N) && N){
COUNT=0;
for(int i=0;i<N;i++)
scanf("%d",&A[i]);
Merge_sort(A,0,N,T);
printf("%I64d\n",COUNT);
}
return 0;
}