1. 程式人生 > >演算法1:求逆序對數與顯著逆序對數(歸併排序)

演算法1:求逆序對數與顯著逆序對數(歸併排序)

寫在前面:由本文開始記錄本人的演算法刷題之路,日後會不定期更新,歡迎討論!本系列系本人原創,如需轉載或引用,請註明原作者及文章出處。
求逆序對數問題是歸併排序的基礎問題,顯著逆序對數則是逆序對數的升級版。POJ2299,POJ1804均是此類問題(但是個別細節不同,例如POJ2299需要將逆序對數變數num設為long long int型)。 一、求逆序對數 描述 對於一個長度為N的整數序列A,滿足i < j 且 Ai > Aj的數對(i,j)稱為整數序列A的一個逆序。請求出整數序列A的所有逆序對個數
輸入 輸入包含多組測試資料,每組測試資料有兩行
第一行為整數N(1 <= N <= 20000),當輸入0時結束
第二行為N個整數,表示長為N的整數序列
輸出 每組資料對應一行,輸出逆序對的個數 樣例輸入51 2 3 4 555 4 3 2 1110樣例輸出0100解體思路

歸併排序,大家看程式碼自行理解吧。

程式碼

#include<iostream>
#include<algorithm>
using namespace std;
int n, a[20010], temp[20010], num;

void merge(int begin, int mid, int end)
{
	int i = begin;
	int j = mid + 1;
	int k = begin;

	while (i <= mid && j <= end)
	{
		if (a[i] > a[j])
		{
			temp[k] = a[j];
			k++;
			j++;
			num += mid - i + 1;
		}
		else
		{
			temp[k] = a[i];
			k++;
			i++;
		}
	}

	while (i <= mid)
	{
		temp[k] = a[i];
		k++;
		i++;
	}

	while (j <= end)
	{
		temp[k] = a[j];
		k++;
		j++;
	}

	for (int p = begin; p <= end; p++)
		a[p] = temp[p];
}

void mergesort(int begin, int end)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	mergesort(begin, mid);
	mergesort(mid + 1, end);
	merge(begin, mid, end);
}

int main()
{
	while (1)
	{
		cin >> n;
		if (n == 0)
			break;

		for (int i = 0; i < n; i++)
			cin >> a[i];

		num = 0;
		mergesort(0, n - 1);

		cout << num << endl;
	}
	return 0;
}
二、求顯著逆序對數 描述 對於一個長度為N的整數序列A,滿足i < j 且 Ai > 2 * Aj的數對(i,j)稱為整數序列A的一個顯著逆序。請求出整數序列A的所有顯著逆序對個數
輸入 輸入包含多組測試資料,每組測試資料有兩行
第一行為整數N(1 <= N <= 20000),當輸入0時結束
第二行為N個整數,表示長為N的整數序列
輸出 每組資料對應一行,輸出顯著逆序對的個數 樣例輸入51 2 3 4 555 4 3 2 1612 10 8 6 4 20樣例輸出046
解體思路 與求逆序對數不同,由於Ai > 2 * Aj,因此需要將排序和計數分別操作。也就是先做count,再做merge。
程式碼
#include<iostream>
#include<algorithm>
using namespace std;
int n, a[20010], temp[20010], num;

void mcount(int begin, int mid, int end)
{
	int i = begin;
	int j = mid + 1;
	int k = begin;
	while (i <= mid && j <= end)
	{
		if (a[i] > 2 * a[j])
		{
			num += mid - i + 1;
			j++;
		}
		else
			i++;
	}
}

void merge(int begin, int mid, int end)
{
	int i = begin;
	int j = mid + 1;
	int k = begin;

	while (i <= mid && j <= end)
	{
		if (a[i] > a[j])
		{
			temp[k] = a[j];
			k++;
			j++;
		}
		else
		{
			temp[k] = a[i];
			k++;
			i++;
		}
	}

	while (i <= mid)
	{
		temp[k] = a[i];
		k++;
		i++;
	}

	while (j <= end)
	{
		temp[k] = a[j];
		k++;
		j++;
	}

	for (int p = begin; p <= end; p++)
		a[p] = temp[p];
}

void mergesort(int begin, int end)
{
	if (begin >= end)
		return;

	int mid = (begin + end) / 2;
	mergesort(begin, mid);
	mergesort(mid + 1, end);
	mcount(begin, mid, end);
	merge(begin, mid, end);
}

int main()
{
	while (1)
	{
		cin >> n;
		if (n == 0)
			break;

		for (int i = 0; i < n; i++)
			cin >> a[i];

		num = 0;
		mergesort(0, n - 1);

		cout << num << endl;
	}

	return 0;
}