寻找“捣乱分子”对[

来源:互联网 发布:c罗梅西 知乎 编辑:程序博客网 时间:2024/04/28 21:33

转载:http://blog.csdn.net/wuzhekai1985/article/details/6718256

 

问题描述:多人排成一个队列,我们认为从低到高是正确的序列,但是总有部分人不遵守秩序。如果说,前面的人比后面的人高(两人身高一样认为是合适的),那么我们就认为这两个人是一对“捣乱分子”,比如说,现在存在一个序列:176, 178, 180, 170, 171
      这些捣乱分子对为<176, 170>, <176, 171>, <178, 170>, <178, 171>, <180, 170>, <180, 171>,那么,现在给出一个整型序列,请找出这些捣乱分子对的个数(仅给出捣乱分子对的数目即可,不用具体的对)

      思路:最简单的就是用两层循环,即考察每个元素,检查该元素后面的元素是否小于它,如果是就找到一个捣乱分子对。复杂度为O(n^2)。这道题的一种改进方案是用分治法,用到了归并排序的思想。我们可以将数组分成两部分,总的捣乱分子对为: 前半部分的捣乱分子对 + 后半部分的捣乱分子对 + X,这里的X为两部分合并中出现的捣乱分子对,什么情况下会出现呢?假设前半部分的元素范围为 A[from] 到A[mid],后半部分的元素范围为A[mid + 1] 到A[to],考虑前半部分的某元素A[i]和后半部分的某元素A[j],如果A[j] < A[i],由于两部分都是排好序的,因此捣乱分子对增加 mid - i + 1个,也就是说A[j]小于A[i], A[i+1].. A[mid],这个关系显然成立。

 

#include<iostream>using namespace std;int CalcAndMerge(int arr[],int left,int mid,int right);int FindTrouble(int arr[],int left,int right);int FindTrouble(int arr[],int left,int right){if(left>=right)return 0;int mid = left+(right-left)/2;return FindTrouble(arr,left,mid)+FindTrouble(arr,mid+1,right)+CalcAndMerge(arr,left,mid,right);}int CalcAndMerge(int arr[],int left,int mid,int right){int left1=left,right1=mid,left2=mid+1,right2=right;if(left1>right1 || left2>right2)return 0;int i=left1,j=left2;int sum=0;while(i<=right1 && j<=right2){while(i<=right1 && arr[i]<=arr[j])i++;if(i<=right1){sum+=right1-i+1;j++;}}//mergei=left1;j=left2;int *tmp=new int[right-left+1];int t=0;while(i<=right1 && j<=right2){if(arr[i]<arr[j])tmp[t++]=arr[i++];elsetmp[t++]=arr[j++];}while(i<=right1)tmp[t++]=arr[i++];while(j<=right2)tmp[t++]=arr[j++];for(t=0,i=left;i<=right;t++,i++)arr[i]=tmp[t];return sum;}int main(){int arr[]={5,3,7,5,1,12,9,7,2,9,9,4,1,7,3,6,10,6};int output = FindTrouble(arr,0,sizeof(arr)/sizeof(int)-1);cout<<output<<endl;return 0;}

原创粉丝点击