zoukankan      html  css  js  c++  java
  • 浅谈逆序对

    什么是逆序对

    (A) 为一个有$ n (个数字的有序集) (n>1)(,其中所有数字各不相同。 如果存在正整数) i, j$ 使得 (1 ≤ i < j ≤ n) 而且 (A[i] > A[j]),则 (<A[i], A[j]>) 这个有序对称为 $A $的一个逆序对,也称作逆序数。by百度百科

    怎么求逆序对

    1.暴力求解

    最原始的方法,利用两重循环进行枚举。该算法的时间复杂度为(O(n^2))

    int doit(int *a, int N)
    {
        int count = 0;
        int i, j;
        for(i=0; i<N-1; i++)
            for(j=i+1; j<N; j++)
                if(a[i]>a[j])
                count++;
        return count;
    }
    

    p党福利

    var
      i,j,k,n:longint;
      a:array[1..1000000] of longint;
    begin
      readln(n);
      for i:=1 to n do read(a[i]);
      k:=0;
      for i:=1 to n-1 do
      for j:=i+1 to n do
      if a[i]>a[j] then inc(k);
      writeln(k);
    end.
    

    2.归并排序

    也就是这样了,不想写了,时间复杂度为(O(n imes log(n)))

    这里就用一下luogu题解第一篇的解释

    #include<iostream>
    #include<cstring>
    using namespace std;
    long long int a[1000001];
    long long int tot;
    long long int n;
    long long int ans[1000001];
    long long int now;
    void f(long long int s,long long int t) {
    	if(s==t)return;
    	int mid=(s+t)/2;
    	f(s,mid);
    	f(mid+1,t);
    	long long int i=s;
    	long long int j=mid+1;
    	now=s;
    	while(i<=mid&&j<=t) {
    		if(a[i]<=a[j]) {
    			ans[now]=a[i];
    			i++;
    			now++;
    		} else {
    			tot=tot+mid-i+1;
    			ans[now]=a[j];
    			j++;
    			now++;
    		}
    	}
    	while(i<=mid) {
    		ans[now]=a[i];
    		i++;
    		now++;
    	}
    	while(j<=t) {
    		ans[now]=a[j];
    		j++;
    		now++;
    	}
    	for (i=s; i<=t; i++)
    		a[i] = ans[i];
    
    }
    int main() {
    	long long int n;
    	cin>>n;
    	for(int i=1; i<=n; i++)
    		cin>>a[i];
    	f(1,n);
    	cout<<tot;
    	return 0;
    }
    

    3.树状数组

    将原数组从大到小排好序,然后依次取出最大的,次大的...按坐标插入到树状数组中
    当前取出的元素为a
    此时所有比a大的元素已经插入到树状数组中了,现在只统计有多少元素在a的前面就好了。

    朴素代码

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<cctype>
    by mjt
    using namespace std;
    
    const int N = 100010;
    
    struct Node{
        int val,pos;
        bool operator < (const Node &a) const {
            return val > a.val;
        }
    }a[N];
    int sum[N],n;
    
    inline int read() {
        int x = 0,f = 1;char ch=getchar();
        for (; !isdigit(ch); ch=getchar()) if(ch=='-')f=-1;
        for (; isdigit(ch); ch=getchar()) x=x*10+ch-'0';
        return x*f;
    }
    void update(int p,int x) {
        for (; p<=n; p+=p&(-p)) sum[p] += x;
    }
    int query(int p) {
        int ans = 0;
        for (; p>=1; p-=p&(-p)) ans += sum[p];
        return ans;
    }
    int main() {
        n = read();
        for (int i=1; i<=n; ++i) 
            a[i].val = read(),a[i].pos = i;
        sort(a+1,a+n+1);
        int ans = 0;
        for (int i=1; i<=n; ++i) {
            ans += query(a[i].pos-1);
            update(a[i].pos,1);
        }
        cout << ans;
        return 0;
    }
    

    上面的代码只有35分?
    为什么?

    优化版

    上面的代码没有处理重复的问题,导致大量的重复添加操作
    所以我们只需要去重就行

    解释一个函数(unique)
    点这里你就明白了

    unique函数属于STL中比较常用函数,它的功能是元素去重。即”删除”序列中所有相邻的重复元素(只保留一个)。此处的删除,并不是真的删除,而是指重复元素的位置被不重复的元素给占领了(详细情况,下面会讲)。由于它”删除”的是相邻的重复元素,所以在使用unique函数之前,一般都会将目标序列进行排序。

    如把(1,2,2,2,2,2,2,5,6)去重

    就变成了(1,2,5,6,2,2,2,2,2)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<map>
    #include<string>
    #include<cstring>
    #define int long long int
    #define lowbit(x) x & -x
    const int N=5e5+10;
    using namespace std;
    inline int read() {
    	char c = getchar();
    	int x = 0, f = 1;
    	while(c < '0' || c > '9') {
    		if(c == '-') f = -1;
    		c = getchar();
    	}
    	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return x * f;
    }
    int ans;
    int tree[N],n,a[N],b[N];
    void add(int x)
    {
    	while(x<=n)
    	{
    		tree[x]++;
    		x+=lowbit(x);
    	}
    }
    int query(int x)
    {
    	int ans=0;
    	while(x>0)
    	{
    		ans+=tree[x];
    		x-=lowbit(x);	
    	}
    	return ans;
    }
    signed main()
    {
    	n=read();
    	for(int i=1;i<=n;++i) a[i]=read(),b[i]=a[i];
    	sort(a+1,a+1+n);
    	int len=unique(a+1,a+1+n)-a-1;
    	for(int i=1;i<=n;++i)
    	{
    		int p=lower_bound(a+1,a+1+len,b[i])-a;
    		add(p);
    		ans+=i-query(p);
    	}
    	cout<<ans;
    	return 0;
    }
    
    

    插入求逆序对?!我也不知道应该叫什么

    首先我们想一下冒泡排序的过程,我们不难发现,对于每一个元素,我们实际上是让他不停的和前面的元素比较,交换。

    也正是因为这个过程决定了在冒泡排序的过程中:一个位置的数的前面的数一定是递增的(从小到大排的话)

    那么我们在交换的时候,直接二分找到一个合适的位置,插入即可

    这个很显然可以用平衡树Vector实现

    代码也非常短
    by lgj学长

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    int n,m,ans,a[100001];
    vector<int>v;
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)    
    	    scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            int now=upper_bound(v.begin(),v.end(),a[i])-v.begin();
            ans=ans+i-now-1,v.insert(v.begin()+now,a[i]);
        }
        printf("%d",ans);
        return 0;
    }
    

    两倍经验
    点这里

  • 相关阅读:
    qs.parse()、qs.stringify() 、JSON.stringify()以及JSON.parse()的区别
    Vue设置button的disable属性
    vue-preview用法详解
    解决MUI不支持js严格模式环境下使用的问题
    Vue.js的keep-alive用法
    详解在webpack中使用axios
    详解VS2012发布web api流程
    解决Python2中文ascii编码的方法
    防火墙问题 Linux系统 /etc/sysconfig/路径下无iptables文件
    本机连接虚拟机中docker启动的mysql数据库
  • 原文地址:https://www.cnblogs.com/pyyyyyy/p/11112289.html
Copyright © 2011-2022 走看看