欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

POJ 2299 Ultra-QuickSort(求逆序对)

程序员文章站 2022-05-10 15:37:07
...

题目链接:http://poj.org/problem?id=2299

题目大意:给定一个数组,按冒泡排序规则,问需要交换多少次,才能成为有序序列


题目思路:就是求逆序对的数量就是交换次数,因为每一前面大的数都会和后面小的交换一次


求逆序对有三种方法:归并排序,树状数组,线段树

可参见:https://blog.csdn.net/baodream/article/details/80355881

第一种:归并排序求逆序对

#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>

using namespace std;

#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)

const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const double DINF = 0xffffffffffff;
const int mod = 1e9+7;
const int N = 1e6+5;

int a[N];     //需要排序的数组
int temp[N];   //中间合并用数组
ll g_nCount;  //逆序对数量

void MergeArray(int l,int r,int mid) //合并两序列
{
    int i=l,n=mid;    //左子序列指针
    int j=mid+1,m=r;  //右子序列指针
    int k=0;          //temp数组指针
    while(i<=n&&j<=m)
    {
        if(a[i]<=a[j])
            temp[k++] = a[i++];
        else
        {
            temp[k++] = a[j++];
            //a[j]和前面每一个数都能组成逆序数对
            g_nCount+=n-i+1;
        }
    }
    while(i<=n)
        temp[k++] = a[i++];
    while(j<=m)
        temp[k++] = a[j++];
    for(int t=0;t<k;t++)   //拷贝回去
        a[l+t] = temp[t];
}

void Merge_sort(int l,int r) //归并排序,0~n-1的数组,则l=0,r=n-1
{
    if(l<r)
    {
        int mid = (l+r)>>1;
        Merge_sort(l,mid);   //左子序列排序
        Merge_sort(mid+1,r); //右子序列有序
        MergeArray(l,r,mid); //两边合并
    }
}


int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    std::ios::sync_with_stdio(false);
    int n;
    while(cin>>n)
    {
        if(n==0)
            break;
        g_nCount=0;
        for(int i=0;i<n;i++)
            cin>>a[i];
        Merge_sort(0,n-1);
        cout<<g_nCount<<endl;
    }
    return 0;
}

第二种:树状数组求逆序对

#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<queue>
#include<stack>
#include<map>

using namespace std;

#define FOU(i,x,y) for(int i=x;i<=y;i++)
#define FOD(i,x,y) for(int i=x;i>=y;i--)
#define MEM(a,val) memset(a,val,sizeof(a))
#define PI acos(-1.0)

const double EXP = 1e-9;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const ll MINF = 0x3f3f3f3f3f3f3f3f;
const double DINF = 0xffffffffffff;
const int mod = 1e9+7;
const int N = 1e6+5;

int tree[N];
int n;
struct node
{
    int val;  //输入数据的值
    int id;   //输入数据的位置
}a[N];

int b[N];     //离散化数组

bool cmp(node A,node B)
{
    if(A.val==B.val)
        return A.id<B.id;
    return A.val<B.val;
}

int lowbit(int x)
{
    return x & (-x);
}

int getsum(int x)
{
    int res = 0;
    while(x)
    {
        res+=tree[x];
        x-=lowbit(x);
    }
    return res;
}

void add(int x,int val)
{
    while(x<=n)
    {
        tree[x]+=val;
        x+=lowbit(x);
    }
}

void init()
{
    for(int i=1;i<=n;i++)
        tree[i]=0;
}

ll get_Reverse_pair_count()
{
    init();  //初始化树状数组
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)  //离散化操作
        b[a[i].id]=i;      //将a[i]数组映射成更小的值
    ll ans=0;
    for(int i=1;i<=n;i++)  //更新树状数组
    {
        add(b[i],1);       //意思是b[i]的值加1
        ans+=(i-getsum(b[i])); //求得比b[i]小的个数,则i减getsum(b[i])就是前面能够和b[i]构成逆序对的数量
    }
    return ans;
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    std::ios::sync_with_stdio(false);
    while(cin>>n)
    {
        if(n==0)
            break;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i].val;
            a[i].id=i;
        }
        cout<<get_Reverse_pair_count()<<endl;
    }
    return 0;
}


第一种:线段树求逆序对

待补。
相关标签: POJ 求逆序对