【PTA】7-2 逆ペアの数を求めよ(30点)

7-2 逆ペアの数を求めよ(30点)

注: この問題のアルゴリズムの時間計算量は O(nlogn) である必要があります。そうでない場合、スコアは無効になります。

出典:http://poj.org/problem?id=1804
背景 レイモンド・バビットは弟のチャーリーを怒らせます。最近レイモンドさんは、一目見ただけであっという間に床中にこぼれた爪楊枝が 246 本も数えました。そして彼はポーカーカードを数えることさえできます。チャーリーもそのようなクールなことができるようになりたいと思っています。彼は同様の課題で兄に勝ちたいと考えています。
問題 チャーリーが考えていることは次のとおりです。N 個の数値のシーケンスを取得したと想像してください。目標は、数字を移動させて、最終的にシーケンスが順序付けられるようにすることです。許可される唯一の操作は、隣接する 2 つの数字を交換することです。例を試してみましょう: 以下から始めます: 2 8 0 3 スワップ (2 8) 8 2 0 3 スワップ (2 0) 8 0 2 3 スワップ (2 3) 8 0 3 2 スワップ (8 0) 0 8 3 2 スワップ(8 3) 0 3 8 2 スワップ (8 2) 0 3 2 8 スワップ (3 2) 0 2 3 8 スワップ (3 8) 0 2 8 3 スワップ (8 3) 0 2 3 8

翻訳:
これはチャーリーのアイデアです。N 個の数値を含むシーケンスを取得したとします。その目的は、最後に順序が整うように数字を移動することです。許可される唯一の操作は、隣接する 2 つの数字を交換することです。例を試してみましょう:
まず: 2 8 0 3 swap(2 8)
8 2 0 3 swap(2 0)
8 0 2 3 swap(2 3)
8 0 3 2 swap(8 0)
0 8 3 2 swap(8) 3)
0 3 8 2 swap(8 2)
0 3 2 8 swap(3 2)
0 2 3 8 swap(3 8)
0 2 8 3 swap(3 8)
最終的に 0 2 3 8 を取得

したがって、シーケンス (2 8 0 3) は、隣接する数値を 9 回入れ替えることでソートできます。ただし、このような 3 つのスワップを使用して並べ替えることもできます。 開始: 2 8 0 3 swap (8 0) 2 0 8 3 swap (2 0) 0 2 8 3 swap (8 3) 0 2 3
8は次のとおりです。指定されたシーケンスを並べ替えるために、隣接する数値を交換する最小回数はいくらですか?チャーリーにはレイモンドのような精神的能力がないため、不正行為を行うことにしました。ここであなたの出番です。彼は、O(nlogn) で質問に答えるコンピューター プログラムを書いてほしいと頼みます。彼はそれに対してかなりの賞金を支払うので安心してください。

翻訳:
したがって、シーケンス (2 8 0 3) は、9 つ​​の隣接する数値を交換することで並べ替えることができます。ただし、次の 3 つのスワップを使用してソートすることもできます。
start: 2 8 0 3 swap(8 0)
2 0 8 3 swap(2 0)
0 2 8 3 swap(8 3)
returns 0 2 3 8

入力形式:

最初の行にはシーケンスの長さ N (1 <= N <= 1000) が含まれます。2 行目にはシーケンスの N 要素が含まれます (各要素は [-1000000, 1000000] の整数です)。この行のすべての数値は 1 つの空白で区切られます。

翻訳: 最初の行にはシーケンスの長さ N (1 <= N <= 1000) が含まれ、2 行目には
シーケンスの N 要素が含まれます (各要素は [-1000000, 1000000] の整数です)。
この行のすべての数字は単一のスペースで区切られています

出力フォーマット:

指定されたシーケンスを並べ替えるのに必要な、隣接する数値の最小スワップ数を含む 1 行を出力します。

変換: 指定されたシーケンスをソートするために必要な隣接する数字の最小スワップ数を出力します。

入力サンプル:

一連の入力がここに与えられます。例えば:

6
-42 23 6 28 -100 65537
末尾に空行なし

出力例:

対応する出力をここに示します。例えば:

5 最後に空白行はありません

アイデア:

1. 複雑さの要件が O(nlogn) であるため、マージ ソートの方法が採用されます。
2. トピックのタイトルは逆ペアであり、その逆ペアは<a[i], a[j]>です(i<j ですが、a[i]>a[j])
3. 最初のステップは次のとおりです。 mergesort() を使用して配列を次のように分割します。 2 つの半分はソートされ、再帰的に呼び出されます。
2 番目のステップでは、merge() を使用して、別々の配列を再び結合します。
4. merge() では、完全な配列 a が 2 つの半分に分割され、2 つの半分の最初の数、つまり a[i] と a[j] からサイズが比較され、小さい方の配列が代入されます。配列 b に入力します。さらに、a[j] の左側に a[j] より大きい数がある (配列 b に入らない) 場合、それは逆ペアが形成できることを意味し、そのような数の数は次のように記録されます。番号。
5. main 関数は num を出力します。

#include<iostream>
using namespace std;
int num;//逆序对个数
int a[10005],b[10005];

int merge(int a[],int b[],int l,int m,int r){
    
    
    int i=l,j=m+1,k=l;//k:数组b的下标
    //将2803分成两个数组(且两数组已经分别排好序),前面一半28,后面一半03,从两个数组的第一个数字开始比较,所以i=l,j=m+1
    while((i<=m)&&(j<=r)){
    
    
        if(a[i] <= a[j]){
    
    
            b[k++]=a[i++];//左右比较,将较小的数字放入数组b
        }else{
    
    
            b[k++]=a[j++];
            num+=m+1-i;//num表示在a[j]左边的数字中(除了已经进入b数组的数字),有多少个数字可以和a[j]组成逆序对(即比a[j]大)
        }
    }
    if(i>m){
    
    //将右边剩下的都填入b
        for(int q=j;q<=r;q++){
    
    
            b[k++]=a[q];
        }
    }else{
    
    //将左边剩下的都填入b
        for(int q=i;q<=i;q++){
    
    
            b[k++]=a[q];
        }
    }
    for(int q=l;q<=r;q++){
    
    //复制回a数组
        a[q]=b[q];    }
}

int mergesort(int a[],int l,int r){
    
    
    if(r>l){
    
    
        int m=(r+l)/2;
        mergesort(a,l,m);
        mergesort(a,m+1,r);
        merge(a,b,l,m,r);
    }
}

int main(){
    
    
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
    
    
        cin>>a[i];
    }
    mergesort(a,0,n-1);
    cout<<num;
}

おすすめ

転載: blog.csdn.net/qq_51669241/article/details/120469588