분할 및 정복 : 역순으로 계산 문제

분할 및 정복 : 역순으로 계산 문제

질문 :
길이가 n 인 배열을 입력하고 배열의 총 역쌍 수를 A[n]찾으 A[n]십시오.
입력 :
길이 n의 배열 A [n]
출력 : 배열 A [n]의
총 역방향 쌍 수

문제 분석 :

여기에 사진 설명 삽입

  1. 배열 A를 두 개의 하위 배열 A [1… n / 2], A [n / 2 + 1… n]로 나눕니다.
  2. 재귀 적으로
    해결하는 하위 문제 Sl : 역방향
    풀이 횟수의 A [1 ... n / 2] S2 : 만 [... n n / 2 + 1 ] 역순의 횟수
  3. 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;
}

추천

출처blog.csdn.net/qq_44524918/article/details/108932680