분할 및 정복 : 역순으로 계산 문제
질문 :
길이가 n 인 배열을 입력하고 배열의 총 역쌍 수를 A[n]
찾으 A[n]
십시오.
입력 :
길이 n의 배열 A [n]
출력 : 배열 A [n]의
총 역방향 쌍 수
문제 분석 :
- 배열 A를 두 개의 하위 배열 A [1… n / 2], A [n / 2 + 1… n]로 나눕니다.
- 재귀 적으로
해결하는 하위 문제 Sl : 역방향
풀이 횟수의 A [1 ... n / 2] 만 S2 : 만 [... n n / 2 + 1 ] 역순의 횟수 - A [1… n / 2]와 A [n / 2 + 1… n]의 해
를 결합합니다. S3에 대한 풀기 : 부분 배열에서 역쌍의 수
S = S1 + S2 + S3
가장 큰 하위 배열의 문제와 마찬가지로 실행 효율성의 병목 현상 값은 병합 측면, 즉 하위 배열 전체에서 S3 의 반전 된 쌍 수 를 해결 하는 문제에도 있습니다.
S3 솔루션 :
1. 전략 1 : 직접 풀기
(1) 각 Aj] ∈ A [m + 1… n], A [i] ∈ A [1… m]을 열거하고 역순으로 쌍의 수를 세십시오.
(2) S를 푸는 알고리즘 실행 시간 : O (n 2 )
직접 솔루션 의 실행 시간이 이상적이지 않은데 더 좋은 방법이 있습니까?
2. 전략 II : 정렬 용액
(1)은 각각 상기 어레이 A의 [1 ... M] 및 A [m + 1 ... N]을위한 선별 ;
(2) 각각 A [J] ∈A를 들어 [m + 1 ... N], 이진 검색 을 사용 하여 A [1… m]에서 찾습니다 .
(3) A [1… m] 앵커 포인트의 오른쪽에있는 A [j] 요소는 A [j]와 역쌍을 형성 할 수 있습니다.
(4) 해결 S'algorithm 실행 시간 : O (nlogn).
S 나누기 및 정복을 해결하기 위해 정렬하면 알고리즘의 실행 시간이 향상되지만 여전히 최적화 가능성이 있습니다.
3. 전략 3 : 솔루션 병합
(1) 왼쪽에서 오른쪽으로 정렬 된 하위 배열 스캔 : A [i] ∈A [1… m], A [j] ∈ A [m + 1… n]
··· If A [i]> A [j], 역순 쌍 계산, j를 오른쪽으로 이동
... A [i] ≤ A [j] 인 경우 i를 오른쪽으로 이동
(2) 병합 정렬 프레임 워크를 사용하여 병합 된 배열의 순서를 확인합니다.
(3) S 시간 복잡도가 O (n)으로 감소
알고리즘 분석 다이어그램 :
암호
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 10000;
int a[N],b[N];
int count;
void Merge(int a[], int l, int mid , int r) //归并排序的合并部分
{
int i = l, j = mid + 1,k = l;
//遍历统计逆序数对
while(i <= mid&&j <= r){
if(a[i] <= a[j])
b[k++] = a[i++];
else{
count += j - k;
b[k++] = a[j++];
}
}
while(i <= mid) //左半部分排序
b[k++] = a[i++];
while(j <= r) //右半部分排序
b[k++] = a[j++];
for(int i = l; i <= r; i++) //从辅助空间复制回 a 数组
a[i] = b[i];
}
void MergeSort(int a[], int l, int r) //归并排序
{
if(l < r){
int mid = l + (r-l)/2;
MergeSort(a,l,mid); // 左半部分递归求解
MergeSort(a,mid+1,r); // 右半部分递归求解
Merge(a,l,mid,r); // 合并前后两个部分
}
}
int main(){
int n;
cin >> n;
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
MergeSort(a,0,n-1);
cout << count;
return 0;
}
예 : 역서 수 찾기
제목 설명
배열에서 한 쌍의 숫자의 앞과 뒤 위치가 반대 순서, 즉 앞의 숫자가 뒤의 숫자보다 크면 역순이라고합니다. 약정의 총 역순 수를 약정의 역수라고합니다.
이제 N 개의 요소의 시퀀스가 주어 졌을 때 역서 수를 판단하십시오.
예를 들어, 1 3 2의 역수는 1입니다.
입력
테스트 데이터 그룹 수를 나타내려면 첫 번째 줄에 정수 T를 입력합니다 (1 <= T <= 5)
. 각 테스트 데이터 그룹의 각 행은 정수입니다. N은 시퀀스에 N 개의 요소가 있음을 나타냅니다 (2 <= N <= 1000000).
다음 행에는 시퀀스의 모든 요소를 나타내는 N 개의 정수 Ai (0 <= Ai <1000000000)가 있습니다.
데이터 보장 : 여러 테스트 데이터 세트 중 100,000 개가 넘는 테스트 데이터 세트는 최대 한 세트입니다.
출력
시퀀스의 역 번호 출력
샘플 입력
2
2
1 1
3
1 3 2
샘플 출력
0
1
암호:
#include <iostream>
#include <cstdio>
using namespace std;
#define max 1000001
long long a[max],b[max];
long long count;
void Merge(long long a[], int l, int mid , int r) //归并排序的合并部分
{
int i = l, j = mid + 1,k = l;
//遍历统计逆序数对
while(i <= mid&&j <= r){
if(a[i] <= a[j])
b[k++] = a[i++];
else{
count += j - k;
b[k++] = a[j++];
}
}
while(i <= mid) //左半部分排序
b[k++] = a[i++];
while(j <= r) //右半部分排序
b[k++] = a[j++];
for(int i = l; i <= r; i++) //从辅助空间复制回 a 数组
a[i] = b[i];
}
void MergeSort(long long a[], int l, int r) //归并排序
{
if(l < r){
int mid = l + (r-l)/2;
MergeSort(a,l,mid); // 左半部分递归求解
MergeSort(a,mid+1,r); // 右半部分递归求解
Merge(a,l,mid,r); // 合并前后两个部分
}
}
int main(){
int n;
for(int i = 0; i < n; i++)
scanf("%d",&a[i]);
MergeSort(a,0,n-1);
printf("%lld\n",count);
return 0;
}
int main(){
int n,m;
scanf("%d",&n);
while(n--){
scanf("%d",&m);
count = 0;
for(int i = 0; i < m; i++)
scanf("%d",&a[i]);
MergeSort(a,0,m-1);
printf("%lld\n",count);
}
return 0;
}