1. 程式人生 > >歸併排序求逆序數(POJ 1804,POJ 2299,HDU 4911)

歸併排序求逆序數(POJ 1804,POJ 2299,HDU 4911)

首先,明確兩個概念:

逆序對:數列a[1],a[2],a[3]…中的任意兩個數a[i],a[j] (i<j),如果a[i]>a[j],那麼我們就說這兩個數構成了一個逆序對.

逆序數:一個數列中逆序對的總數.

例題一:POJ 1804.   點選開啟連結

解題思路:每次交換隻能減少一個逆序,而且必定能減少一個逆序,從而問題就轉換為求逆序個數了這題資料規模很小,暴力可過。

我這裡提供了用Merge_sort的方法求解逆序數,時間複雜度為O(nlogn).

關於歸併排序:歸併排序是將數列a[l,r]分成兩半a[l,mid]和a[mid+1,r]分別進行歸併排序,然後再將這兩半合併起來。

在合併的過程中(設l<=i<=mid,mid+1<=j<=r),當a[i]<=a[j]時,並不產生逆序數;當a[i]>a[j]時,在前半部分比a[i]大的數都比a[j]大,將a[j]放在a[i]前面的話,逆序數要加上mid+1-i。因此,可以在歸併序中的合併過程中計算逆序數。

#include<cstdio>
#define N 1000+10

int ans=0;
int f[N],t[N];
void Merge(int l,int m,int r) //左右兩個表合併成一個表
{
    int i=l,j=m+1,cnt=0;
    while(i<=m && j<=r)
    {
        if(f[i]<=f[j])
            t[cnt++]=f[i++];
        else
        {
            ans+=m-i+1;
            t[cnt++]=f[j++]; //核心程式碼,求解逆序數個數。
        }
    }
    while(i<=m) //若左表不空
        t[cnt++]=f[i++];
    while(j<=r) //若右表不空
        t[cnt++]=f[j++];
    for(int k=0;k<cnt;) //修改原陣列
        f[l++]=t[k++];
}
void Merge_sort(int l,int r) //歸併排序
{
    if(l==r)
        return ;
    else
    {
        int m=(l+r)>>1;
        Merge_sort(l,m);
        Merge_sort(m+1,r);
        Merge(l,m,r);
    }
}
int  main()
{
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",&f[i]);
        ans=0;
        Merge_sort(0,n-1);
        printf("Scenario #%d:\n%d\n\n",++cas,ans);
    }
    return 0;
}


例題2:POJ 2299.    點選開啟連結

跟上題沒有什麼區別,但n較大,O(n^2)的演算法必然超時,故必須使用Merge_sort,同時注意要使用 long long 。

#include<cstdio>
#define N 500000+10

long long ans=0;
int f[N],t[N];
void Merge(int l,int m,int r)
{
    int i=l,j=m+1,cnt=0;
    while(i<=m && j<=r)
    {
        if(f[i]<=f[j])
            t[cnt++]=f[i++];
        else
        {
            ans+=m-i+1;
            t[cnt++]=f[j++];
        }
    }
    while(i<=m)
        t[cnt++]=f[i++];
    while(j<=r)
        t[cnt++]=f[j++];
    for(int k=0;k<cnt;)
        f[l++]=t[k++];
}
void Merge_sort(int l,int r)
{
    if(l==r)
        return ;
    else
    {
        int m=(l+r)>>1;
        Merge_sort(l,m);
        Merge_sort(m+1,r);
        Merge(l,m,r);
    }
}
int  main()
{
    int n;
    while(scanf("%d",&n),n)
    {
        for(int i=0;i<n;i++)
            scanf("%d",&f[i]);
        ans=0;
        Merge_sort(0,n-1);
        printf("%lld\n",ans);
    }
    return 0;
}


例題3: hdu 4911.  點選開啟連結

這裡題目另設條件,只能交換k次,所以,先用Merge_sort求出ans,然後與k進行比較即可(ans<=k  ->0 ;  ans>k  -> ans-k ;)。

#include<cstdio>
#define N 500000+10

long long ans=0;
int f[N],t[N];
void Merge(int l,int m,int r)
{
    int i=l,j=m+1,cnt=0;
    while(i<=m && j<=r)
    {
        if(f[i]<=f[j])
            t[cnt++]=f[i++];
        else
        {
            ans+=m-i+1;
            t[cnt++]=f[j++];
        }
    }
    while(i<=m)
        t[cnt++]=f[i++];
    while(j<=r)
        t[cnt++]=f[j++];
    for(int k=0;k<cnt;)
        f[l++]=t[k++];
}
void Merge_sort(int l,int r)
{
    if(l==r)
        return ;
    else
    {
        int m=(l+r)>>1;
        Merge_sort(l,m);
        Merge_sort(m+1,r);
        Merge(l,m,r);
    }
}
int  main()
{
    int n,k;
    while(scanf("%d%d",&n,&k)==2)
    {
        for(int i=0;i<n;i++)
            scanf("%d",&f[i]);
        ans=0;
        Merge_sort(0,n-1);
        if(ans>k) printf("%lld\n",ans-k);
        else printf("0\n");
    }
    return 0;
}


相關推薦

歸併排序序數POJ 1804,POJ 2299,HDU 4911

首先,明確兩個概念: 逆序對:數列a[1],a[2],a[3]…中的任意兩個數a[i],a[j] (i<j),如果a[i]>a[j],那麼我們就說這兩個數構成了一個逆序對. 逆序數:一個數列中逆序對的總數. 例題一:POJ 1804.   點選開啟連結 解題思

歸併排序序數模版

#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<map> #include<cstring> #define ll

歸併排序序數排序演算法

歸併排序:歸併排序是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為

【資料結構排序】POJ1804——歸併排序序數

問題描述: 給定一個數組,問最少經過多少次交換,才可以使得它有序 求解方法: 實際上就是求該陣列的逆序數,使用歸併排序即可 AC程式碼如下: #include<cstdio> #in

利用歸併排序序數

  假設A[1…n]是一個有n個不同元素的陣列,若i < j 且 A[i] > A[j],則對偶(i, j)稱為A的一個逆序對。例如,對於陣列[2, 3, 8, 6, 1],它的所有逆序對為(1, 5),(2, 5),(3, 4),(3, 5),(4

排序演算法之歸併排序及利用歸併排序序數

排序演算法之歸併排序 1. 自頂向下的歸併排序 中心思想 將待排序的陣列平均切分為兩半,將前半部分和後半部分分別進行排序,再講兩個有序陣列歸併到一個數組中 特點 遞迴,需要額外的空間(輔助陣列)來儲存切分完成的子陣列,主要難點在於合併

資料結構實驗之排序五:歸併序數SDUT 3402

歸併排序詳解(戳我)。 以下是搬了別人的。 #include<stdio.h> #include<stdlib.h> long long sum = 0; int a[100005]; int temp[100005]; void Merge(int s1

ACM_小明滾出去?歸並排序序數

思路 name ont ans void long 輸出 lld sort 小明滾出去? Time Limit: 2000/1000ms (Java/Others) Problem Description: 老師:“小明,寫一個排序算法”; 小明: void mys

ACM ICPC 2011–2012, NEERC, Northern Subregional Contest J. John’s Inversions合併排序序數對數

題目連結:http://codeforces.com/gym/100609/attachments 題目大意:有n張牌,每張牌有紅色和藍色兩面,兩面分別寫了一些數字,同種顏色的任意兩個數字若排在前面的

hdu 4911 Inversion歸併排序序對數2014多校訓練第5場

Inversion                                                                             Time Limit: 2000/1000 MS (Java/Others)    Memory L

【模板】歸併排序序對

歸併排序求逆序對 1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdio> 5 6 typedef long lon

洛谷P1908歸併排序序對

#include<bits/stdc++.h> #define ll long long #define INF 0x3f3f3f3f using namespace std; int n,a[500010],c[500010]; ll ans=0; void msort(int b

歸併排序(序對)

#include<bits/stdc++.h> using namespace std; void merge(int a[],int first,int mid,int last,int temp[]) { int i=first,j=mid+1; in

二分歸併 排序 序對

題目:The Number of Inversions 題面: For a given sequence , the number of pairs  where  and , is called the number of inv

分治序數CDQ

歸併排序 #include<bits/stdc++.h> using namespace std; int b[100]; void merge_sort(int l,int r,int *a){ if(l==r)return; int m=(l+r)>>1; mer

HDU 2689 Sort it 歸併排序序對

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=2689 思路:就是歸併排序啦。在歸併排序的同時,記錄逆序對就行了。時間複雜度即為歸併排序的複雜度:O(N log N)。 程式碼: #include<iostream>

利用歸併排序序對

在逆序對的問題中,如果採用暴力求解的方法,一般也是有效的,但是O(n2)時間複雜度實在是難以接受的。但是對於逆序對問題,卻有一個看似不想關的演算法來解決–歸併排序。時間複雜度和空間複雜度完全與歸併排序一樣,只是在歸併過程中,添加了一個變數,對於逆序對的數目進行了

hdu1394(暴力/線段樹/歸併排序序對的個數)

題目大意:給出一個序列a1, a2, ..., an-1, an (大小為0<=ai-1<=n-1), 如下所示, a1, a2, ..., an-1, an  a2, a3, ..., an, a1  a3, a4, ..., an, a1, a2  ...

poj1804 歸併排序序對

#include<iostream> #include<cstdio> #include<cstdlib> using namespace std; const in

演算法導論2.4 合併排序序數

2-4 逆序對     設A[1...n]是一個包含n個不同數的陣列。如果在i<j的情況下,有A[i]>A[j],則(i,j)就稱為A中的一個逆序對。     (1)列出陣列{2,3,8,6,1}的五個逆序。     (2)如果陣列的元素取自{1,2...,n}