HDU 1394 Minimum Inversion NumberMinimum Inversion Number(線段樹)

http://acm.hdu.edu.cn/showproblem.php?pid=1394

部分來自http://blog.csdn.net/libin56842/article/details/8531117

寫給那些 剛入門線段樹,開始和我一樣對解題迷茫的人.

題意 求最小逆序數

逆序數的概念

在一個排列中,如果一對數的前後位置與大小順序相反,即前面的數大於後面的數,那麼它們就稱爲一個逆序。一個排列中逆序的總數就稱爲這個排列的逆序數。逆序數爲偶數的排列稱爲偶排列;逆序數爲奇數的排列稱爲奇排列。如2 4 3 1中,21,43,41,31是逆序,逆序數是4,爲偶排列。
題目要求序列做如下0 到 n-1變換後, 求出逆序數分別是多少, 輸出 最小的逆序數

如:0 3 4 1 2

設逆序數初始n = 0;

由於0後面沒有比它小的,n = 0

3後面有1,2 n = 2

4後面有1,2,n = 2+2 = 4;

所以該序列逆序數爲 4

其根據題意移動產生的序列有

3 4 1 2 0   逆序數:8

4 1 2 0 3  逆序數:6

1 2 0 3 4  逆序數:2

2 0 3 4 1  逆序數:4

所以最小逆序數爲2


思路:

首先題目規定 輸入的n個數是0到n-1的,不會大於等於n。假設一個序列 a[n],,逆序總數是 m , 第一個數是x, 那麼x的後面有 x 個數比 x 小,因爲包含0, 則第一個數移動最後一個,則 x 的 前面 有 n - x - 1個比 x 大, 所以這時候 逆序總數就是 m - x + n - x - 1;以此類推。 所以,我們只用知道初始序列的逆序數是多少即可, 當然方法很多,這裏講一下線段樹。如何建樹,每輸入一個數a[i],就查詢 比這個數大的區間[a[i]+1, n]內,有多少個數 前面 已經輸入過了, 就是存在,那麼用線段樹便可以解決了。

代碼

#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;

#define manx 5005
struct Tree
{
	int left; 
	int right;
	int num; //該區間內,已經出現的個數
}tree[4 * manx];
int a[manx];

void BuildTree(int i, int l, int r);
void Update(int i, int id);
int Query(int i, int l, int r);

int main()
{
	int n;
	while(~scanf("%d", &n))
	{
		int ans = 0;
		BuildTree(1, 1, n);
		for(int i = 0; i < n; i++)
		{
			scanf("%d", &a[i]);
			Update(1, a[i] + 1);
			ans += Query(1, a[i] + 2, n);//詢問比他大區間已經出現的次數
			//printf("ans = %d\n", ans);
		}
		int m = ans;
		for(int i = 0; i < n; i++)
		{
			m = m + n - 2 * a[i] - 1;
			if(m < ans)
				ans = m;
		}
		printf("%d\n", ans);
	}
	return 0;
}

void BuildTree(int i, int l, int r)//建樹
{
	tree[i].left = l;
	tree[i].right = r;
	tree[i].num = 0;//初始化全部爲0,因爲還沒有輸入
	if(tree[i].left == tree[i].right) return;
	int mid = (l + r) >> 1;
	BuildTree(i << 1, l, mid);
	BuildTree(i << 1 | 1, mid + 1, r);
}

void Update(int i, int id)//更新
{
	if(tree[i].left == id && tree[i].right == id)
	{
		tree[i].num = 1;//輸入過了,標記爲1
		return;
	}
	int mid = (tree[i].left + tree[i].right) >> 1;
	if(id > mid)  Update(i << 1 | 1, id);
	else   Update(i << 1, id);
	tree[i].num = tree[i << 1].num + tree[i << 1 | 1].num;//更新此區間內,已經出現過的總數
}

int Query(int i, int l, int r)//詢問該區間已經出現的次數
{
	//printf("%d %d %d\n", i, l, r);
	if(l > r) return 0;
	if(tree[i].left == l && tree[i].right == r) return tree[i].num;
	int mid = (tree[i].left + tree[i].right) >> 1;

	if(r <= mid) return Query(i << 1, l, r);
	else if(l > mid) return Query(i << 1 | 1, l, r);
	else return Query(i << 1, l, mid) + Query(i << 1 | 1, mid + 1, r);
}                                                        //這裏是mid+1


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章