FZOJ 2166 inversion 逆序数对 模拟

FZOJ 2166 inversion

题目地址

题意

给一个整数序列a1,a2,a3…,an,其逆序数对数为满足 i<j 且 ai>aj 的(i,j)对数。现可以交换其中两个数的位置,问改变后的序列的逆序数对数最小是多少?若不存在一种交换使得序列的逆序数对数变小则答案为原来的逆序数对数。

分析

2<=N <=1000,如果暴力两个数的位置是不行的。 
如果交换i,j位,逆序数对的变化就是: 
原逆序数对 - {(i,j)中与num[i]比较得到的个数} + {(i, j)中与num[j]比较得到的个数},如果num[i]>num[j]还要-1,如果num[i]<num[j]要+1。 
如果无法理解只要画个木棍图,把第i,j之间的木棍都连起来,然后把i,j的木棍交换一下就能懂了。 
我们可以在O(n^2)内预处理出需要的数据,然后再暴力两个位置就行了。

SOLUTION CODE:

/*
*  Author:      illuz <iilluzen[at]gmail.com>
*  File:        2166.cpp
*  Create Date: 2014-04-15 15:03:49
*  Descripton:   
*/

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define RI(x) scanf("%d",&x)
#define rep(i,n) for(int i=0;i<(n);i++)
#define repu(i,a,b) for(int i=(a);i<(b);i++)
#define repd(i,a,b) for(int i=(a);i>=(b);i--)
#define clr(a) memset(a,0,sizeof(a))

const int N = 1010;

int num[N];
int u[N][N];
int n;

int main()
{
	while (~RI(n)) {
		clr(u);
		rep (i, n) {
			RI(num[i]);
		}
		rep (i, n) {
			int tmp = 0;
			repu (j, i + 1, n - 1) {
				if (num[i] < num[j]) tmp++;
				else if (num[i] > num[j]) tmp--;
				u[i][j + 1] = tmp;
			}
		}
		rep (j, n) {
			int tmp = 0;
			repd (i, j - 1, 1) {
				if (num[j] < num[i]) tmp--;
				else if (num[j] > num[i]) tmp++;
				u[i - 1][j] += tmp;
			}
		}
		int ans = 0, rev = 0;
		rep (i, n) repu (j, i + 1, n) {
			if (num[i] > num[j]) rev++;
			if (num[i] < num[j]) u[i][j]++;
			else if (num[i] > num[j]) u[i][j]--;
			ans = min(u[i][j], ans);
		}
		printf("%d\n", rev + ans);
	}
	return 0;
}


你可能感兴趣的:(ACM)