HDU 1394 Minimum Inversion Number(求最小逆序数)

博客探讨了如何解决HDU 1394问题,即找到n个数在重新排列过程中逆序数最小的情况。文章提到可以通过计算原序列逆序数,然后考虑每次将第一个数移到末尾带来的变化,利用归并排序、线段树或树状数组等数据结构来实现。给出了归并排序的解决方案。
摘要由CSDN通过智能技术生成

原题链接:传送门

**题意:**有n个数,依次将第一个数移到最后一个,求这些序列中最小的逆序数。

可以先求出原序列的逆序数,然后依次将第一个数移到最后,这时的逆序数就会减少a[i]个,同时增加n-1-a[i]个。依次求出每一个序列的逆序数,就可以求出这些序列中最小的逆序数了。

这道题可以用线段树来做、也可以用树状数组来做,但既然是求逆序数,那么也可以用归并排序。。


归并排序版:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 5005;
int A[N],B[N];
int ans,sum;
int n;

void Merge(int A[],int l,int m,int r) {
	int i = l;
	int j = m+1;
	int cnt = l;
	while(i <= m && j <= r) {
		if(A[i] <= A[j]) {
			B[cnt++] = A[i++];
		} else {
			B[cnt++] = A[j++];
			sum += j - cnt;
		}
	}
	while(i <= m) {
		B[cnt++] = A[i++];
	}
	while(j <= r) {
		B[cnt++] = A[j++];
	}
	for(int k=l; k<=r; k++) {
		A[k] = B[k];
	}
}

void MergeSort(int A[],int l,int r) {
	if(l == r) {
		return ;
	}
	int m = (r-l)/2 + l;
	MergeSort(A,l,m);
	MergeSort(A,m+1,r);
	Merge(A,l,m,r);
}

int main() {

	while(~scanf("%d",&n)) {
		int temp[n];
		for(int i=0; i<n; i++) {
			scanf("%d",&A[i]);
			temp[i] = A[i];       //还要用个辅助数组来记录原数组,因为排序后原数组就变了。
		}
		sum = 0;
		MergeSort(A,0,n-1);

		ans = sum;
		for(int i=0; i<n; i++) {
			sum = sum - temp[i] + (n-1-temp[i]);
			ans = min(ans,sum);
		}
		printf("%d\n",ans);
	}

	return 0;
}


线段树版:

#include <iostream>
#include <cstring>
#include <cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int N = 5005;
int num[N << 2];
int n,a[N];

void Updata(int pos,int l,int r,int rt){
	num[rt] ++;
	int m = (r-l)/2 + l;
	if(l == r)  return ;
	if(pos <= m)  Updata(pos,lson);
	else  Updata(pos,rson);
}

int Query(int pos,int l,int r,int rt){
	int ans = 0;
	int m = (r-l)/2 + l;
	if(pos == r)  return num[rt];
	if(pos <= m)  ans += Query(pos,lson);
	else  ans += num[rt<<1] + Query(pos,rson);		//要加上左边节点的值 
	return ans;
}

int main(){
	int ans,sum;
	while(~scanf("%d",&n)){
		memset(num,0,sizeof(num));		//空树,直接memset就可以 
		sum = 0;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			a[i] ++;
			Updata(a[i],1,n,1);
			sum += (i-Query(a[i],1,n,1));		//i表示前面已经存i个数, 
		}							//减去a[i]及a[i]的数,就是当前a[i]的逆序数 
		ans = sum;
		for(int i=1;i<=n;i++){
			sum = sum - (a[i]-1) + (n-a[i]);	//把a[i]移到最后 逆序数减少a[i],增加n-1-a[i].
			ans = min(ans,sum);
		}
		printf("%d\n",ans);
	}
	
	return 0;
}


树状数组版:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define lowbit(x) x&(-x)
using namespace std;
const int N = 5005;
int tree[N],a[N];
int n;

void Add(int k,int d) {
	while(k <= n) {
		tree[k] += d;
		k += lowbit(k);
	}
}

int Query(int k) {
	int ret = 0;
	while(k) {
		ret += tree[k];
		k -= lowbit(k);
	}
	return ret;
}

int main() {
	int x;
	int sum,ans;
	while(~scanf("%d",&n)) {
		fill(tree,tree+N,0);	//另一种初始化数组tree的方式
		sum = 0;
		for(int i=1; i<=n; i++) {
			scanf("%d",&a[i]);
			Add(a[i]+1,1);
			sum += (a[i]+1 - Query(a[i]+1));    //**求a[i]之前有几个大于a[i]的数,即逆序数**
		}

		ans = sum;
		for(int i=1; i<=n; i++) {
			sum = sum - a[i] + (n - 1 - a[i]);
			ans = min(ans,sum);
		}
		printf("%d\n",ans);
	}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值