POJ 2299 Ultra-QuickSort(求逆序对)

题目链接: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;
}


扫描二维码关注公众号,回复: 998572 查看本文章

第一种:线段树求逆序对

待补。

猜你喜欢

转载自blog.csdn.net/baodream/article/details/80355848