HDU 3743 Frosh Week

Frosh Week

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)


Problem Description
During Frosh Week, students play various fun games to get to know each other and compete against other teams. In one such game, all the frosh on a team stand in a line, and are then asked to arrange themselves according to some criterion, such as their height, their birth date, or their student number. This rearrangement of the line must be accomplished only by successively swapping pairs of consecutive students. The team that finishes fastest wins. Thus, in order to win, you would like to minimize the number of swaps required.
 

Input
The first line of input contains one positive integer n, the number of students on the team, which will be no more than one million. The following n lines each contain one integer, the student number of each student on the team. No student number will appear more than once. 
 

Output
Output a line containing the minimum number of swaps required to arrange the students in increasing order by student number. 
 

Sample Input
  
  
3 3 1 2
 

Sample Output
  
  
2
 
分析题意后发现是一个求逆序数的问题
给一列数A[1],A[2],…,A[n],求它的逆序数,即有多少个有序对(i,j),使得i<j但A[i]>A[j]。
受归并排序启发,用分治法解决该问题,把序列分成元素个数尽量相等的两半,递归统计i和j均在左边或者均在右边的逆序数,再统计i在左边,但j在右边的逆序数。
对于i在左边,j在右边的分类,按照j的不同把这些“跨越两边”的逆序对进行分类,只要对于右边的每个j,统计左边比它大的元素个数cnt,则所有cnt之和便是答案。归并排序时,当右边的A[j]复制到辅助数组ass中时,左边还没来得及复制到ass的那些数就是左边所有比A[j]大的数。此时cnt加上左边元素个数mid-p即可(左边所剩的元素在区间[p,mid)中,因此元素个数为mid-p)。

归并排序:
(1)划分问题:把序列分成元素个数尽量相等的两半。
(2)递归求解:把两半元素分别进行归并排序。
(3)合并问题:把两个有序表合并成一个。合并时每次只需要把两个序列的最小元素加以比较,删除其中的较小元素并加入合并后的新表即可。

注意小细节
(1)代码中使用的左闭右开区间,用左闭右开区间来表示一个范围,好处是在处理“数组分割”时比较自然,区间[low,high)被分成的是[low,mid)和[mid,high),不需要在任何地方加减1(主函数中调用时high要赋上n+1),另外,空区间表示为[x,x),比[x,x-1]顺眼多了。
(2)分界点mid=low+(high-low)/2而不是(low+high)/2,数学上两者相等,但在计算机中却有差别,运算符“/”的取整是朝零方向的取整,用 low+(high-low)/2来确保分界点总是靠近区间起点(虽然在这题中并非必要),还有一点,使用mid=low+(high-low)/2还可以避免low+high在low和high很大时可能会出现的溢出。
(3)/2用>>1实现

#include <stdio.h>
#define MAX 1000000

int arr[MAX+5],ass[MAX+5];
__int64 cnt;

void MergeSort(int low,int high){
	int mid,p,q,i;
	if(high-low==1)
		return;
	mid=low+((high-low)>>1);
	MergeSort(low,mid);
	MergeSort(mid,high);
	p=low,q=mid,i=low;
	while(p<mid||q<high){
		if(q>=high||(p<mid&&arr[p]<=arr[q]))
			ass[i++]=arr[p++];
		else{
			ass[i++]=arr[q++];
			cnt+=mid-p;
		}
	}
	for(i=low;i<high;i++)
		arr[i]=ass[i];
}

void scani(int &num){
	char ch;
	int flag;
	while(ch=getchar(),(ch>'9'||ch<'0')&&(ch!='-'));
	if(ch=='-')
		flag=-1,num=0;
	else
		flag=1,num=ch-'0';
	while(ch=getchar(),ch<='9'&&ch>='0')
		num=num*10+ch-'0';
	num*=flag;
}

int main(){
	int n,i;
	while(~scanf("%d",&n)){
		for(i=1;i<=n;i++)
			scani(arr[i]);
		cnt=0;
		MergeSort(1,n+1);
		printf("%I64d\n",cnt);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值