Frosh Week

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实现
 1 #include <stdio.h>
 2 #define MAX 1000000
 3 
 4 int arr[MAX+5],ass[MAX+5];
 5 __int64 cnt;
 6 
 7 void MergeSort(int low,int high){
 8     int mid,p,q,i;
 9     if(high-low==1)
10         return;
11     mid=low+((high-low)>>1);
12     MergeSort(low,mid);
13     MergeSort(mid,high);
14     p=low,q=mid,i=low;
15     while(p<mid||q<high){
16         if(q>=high||(p<mid&&arr[p]<=arr[q]))
17             ass[i++]=arr[p++];
18         else{
19             ass[i++]=arr[q++];
20             cnt+=mid-p;
21         }
22     }
23     for(i=low;i<high;i++)
24         arr[i]=ass[i];
25 }
26 
27 void scani(int &num){
28     char ch;
29     int flag;
30     while(ch=getchar(),(ch>'9'||ch<'0')&&(ch!='-'));
31     if(ch=='-')
32         flag=-1,num=0;
33     else
34         flag=1,num=ch-'0';
35     while(ch=getchar(),ch<='9'&&ch>='0')
36         num=num*10+ch-'0';
37     num*=flag;
38 }
39 
40 int main(){
41     int n,i;
42     while(~scanf("%d",&n)){
43         for(i=1;i<=n;i++)
44             scani(arr[i]);
45         cnt=0;
46         MergeSort(1,n+1);
47         printf("%I64d\n",cnt);
48     }
49     return 0;
50 }
View Code

 

posted @ 2015-09-28 09:34  Wei_java  阅读(337)  评论(0编辑  收藏  举报