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;
}