1 solutions

  • 0
    @ 2025-8-11 15:03:49

    最小交换次数题解

    题意:

    一个长度为n的序列,相邻的两个元素可以交换,求最少交换几次才能使序列单调不下降。

    思路:

    不难想到一种纯粹的暴力方法:冒泡排序边排序边统计次数,时间复杂度O(n^2),40分TLE。


    前置知识:逆序对——顾名思义,同一个序列内的两个元素,若元素1为a[i],元素2为a[j],满足1<=i<j<=n且a[j]>a[i],那么称这两个元素互为逆序对。


    有了以上知识,转换思路,因为当两个元素互为逆序对时,进行交换能且仅能消除1个逆序对,也就是——在确定交换能够维护序列相较于原序列更加有序的前提下,交换相邻元素,就是消除1个逆序对。****在不进行“垃圾交换”的前提下,交换次数就是逆序对的个数。 也就是说:最小交换次数其实就是逆序对的个数——— 那么题目就被转换成了统计逆序对的个数! 那么接下来就很简单了————开一个“动态”的前缀和数组,怎么开“动态”前缀和的数组有很多(比如树状数组,哈希等),在此我建立了一个线段树维护一下逆序对个数的前缀和(因为线段树可以动态修改元素)

    - 思路明确,废话不多说,多说不废话,代码如下:

    #include<bits/stdc++.h>
    #define lc k<<1
    #define rc k<<1|1
    using namespace std;
    const long long maxn=1e5;//不开long long见祖宗
    struct Node{
        long long l,r,sum;
    }tr[4*maxn+5];
    int b[maxn];
    long long a[maxn+5];
    void build(long long k,long long l,long long r){//建树
        tr[k].l=l;tr[k].r=r;
        if(l==r){
            tr[k].sum=0;
            return ;
        }
        long long mid=(l+r)/2;
        build(lc,l,mid);
        build(rc,mid+1,r);
        tr[k].sum=tr[lc].sum+tr[rc].sum;
    }
    long long query(int k,int x,int y){//查询x至y区间逆序对的前缀和
        int l=tr[k].l;int r=tr[k].r;
        if(l>=x&&r<=y)return tr[k].sum;
        int maxx=0,mid=(l+r)/2;
        if(x<=mid)maxx+=query(lc,x,y);
        if(y>mid)maxx+=query(rc,x,y);
        return maxx;
    }
    void update(int k,int x,int y){
        int l=tr[k].l;int r=tr[k].r;
        if(l==r){
            tr[k].sum=y;
            return ;
        }
        long long mid=(l+r)>>1;
        if(x<=mid){
            update(lc,x,y);
        }
        else{
            update(rc,x,y);
        }
        tr[k].sum=tr[lc].sum+tr[rc].sum;
    }
    bool cmp(const int x,const int y){//b数组用a数组的大小排序,排编号(后面要查区间)
    	if(a[x]==a[y]){
    		return x<y;
    	}//记得单独考虑重复的情况
    	return a[x]<a[y];
    }
    long long n,ans;
    signed main(){
        scanf("%lld",&n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            b[i]=i;
        }
        build(1,1,n);
        sort(b+1,b+1+n,cmp);
    	for(int i=1;i<=n;i++){
    		if(b[i]!=1)ans+=(b[i]-1)-query(1,1,b[i]-1);//最好要特判(b[i]!=1),因为查询query(1,1,b[i]-1)时若b[i]==1时会死循环
    		update(1,b[i],1);
    	}
    	printf("%lld",ans);//完美收工
        return 0;
    }
      
    

    Information

    ID
    7
    Time
    1000ms
    Memory
    256MiB
    Difficulty
    4
    Tags
    # Submissions
    19
    Accepted
    5
    Uploaded By