分治算法
归并排序
现在对于排序问题用一个sort就欧了,根本就没考虑归并排序这个东东,正好做分治的题目,而归并排序又有分治的思想,所以做了两个水题。。。
其实这个题用冒泡排序做的,但用归并排序也能做出来(分析一下此题与逆序对是有相同之处的)
由于两者的代码完全一样,就只放一个啦 ~\(≧▽≦)/~啦啦啦
1.洛谷 P1116 车厢重组
题目描述
在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转。一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转180度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排列车厢的顺序。于是他就负责用这座桥将进站的车厢按车厢号从小到大排列。他退休后,火车站决定将这一工作自动化,其中一项重要的工作是编一个程序,输入初始的车厢顺序,计算最少用多少步就能将车厢排序。
输入输出格式
输入格式:
输入文件有两行数据,第一行是车厢总数N(不大于10000),第二行是N个不同的数表示初始的车厢顺序。
输出格式:
一个数据,是最少的旋转次数。
输入输出样例
4 4 3 2 1
6
/*----------------------分割线---------------------*/
2.洛谷 P1908 逆序对
题目描述
猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
输入输出格式
输入格式:
第一行,一个数n,表示序列中有n个数。
第二行n个数,表示给定的序列。
输出格式:
给定序列中逆序对的数目。
输入输出样例
6 5 4 2 6 3 1
11
说明
对于50%的数据,n≤2500
对于100%的数据,n≤40000。
思路:
其实思路非常简单,如果用枚举法的话,在数据范围小的时候是可以的,但是某些没良心的出题人,就是不让你简单的拿分啊~ ( ⊙ o ⊙ )啊!
(⊙v⊙)嗯~ 代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 const int N = 40001; 6 int a[N],n,j,len1,len2,b[N],ans; 7 8 void Merge_sort(int l,int r) { 9 if(l==r) return ; 10 int mid = (l+r)/2; 11 Merge_sort(l,mid); 12 Merge_sort(mid+1,r); 13 int i=l,j=mid+1,k=l; 14 while(i<=mid&&j<=r) { 15 if(a[i]<=a[j]) { 16 b[k]=a[i],i++,k++; 17 } 18 else { 19 b[k]=a[j],j++,k++; 20 ans+=mid-i+1; 21 } 22 } 23 while(i<=mid) { 24 b[k]=a[i],k++,i++; 25 } 26 while(j<=r) { 27 b[k]=a[j],k++,j++; 28 } 29 for(int i=l; i<=r; i++) a[i]=b[i]; 30 } 31 int main() { 32 cin>>n; 33 for(int i=1; i<=n; i++) cin>>a[i]; 34 Merge_sort(1,n); 35 cout<<ans<<endl; 36 return 0; 37 }
/*----------------------分割线---------------------*/
3.Codevs 1688 求逆序对
给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目
数据范围:N<=105。Ai<=105。时间限制为1s。
第一行为n,表示序列长度,接下来的n行,第i+1行表示序列中的第i个数。
所有逆序对总数.
4
3
2
3
2
3
(⊙v⊙)嗯~ 代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 const long long N = 4000001; 6 long long a[N],n,j,len1,len2,b[N],ans; 7 8 void Merge_sort(long l,long r) { 9 if(l==r) return ; 10 long long mid = (l+r)/2; 11 Merge_sort(l,mid); 12 Merge_sort(mid+1,r); 13 long long i=l,j=mid+1,k=l; 14 while(i<=mid&&j<=r) { 15 if(a[i]<=a[j]) { 16 b[k]=a[i],i++,k++; 17 } 18 else { 19 b[k]=a[j],j++,k++; 20 ans+=mid-i+1; 21 } 22 } 23 while(i<=mid) { 24 b[k]=a[i],k++,i++; 25 } 26 while(j<=r) { 27 b[k]=a[j],k++,j++; 28 } 29 for(long long i=l; i<=r; i++) a[i]=b[i]; 30 } 31 int main() { 32 cin>>n; 33 for(long long i=1; i<=n; i++) cin>>a[i]; 34 Merge_sort(1,n); 35 cout<<ans<<endl; 36 return 0; 37 }
4.洛谷 P1115 最大子段和
题目描述
给出一段序列,选出其中连续且非空的一段使得这段和最大。
输入输出格式
输入格式:
输入文件maxsum1.in的第一行是一个正整数N,表示了序列的长度。
第2行包含N个绝对值不大于10000的整数A[i],描述了这段序列。
输出格式:
输入文件maxsum1.out仅包括1个整数,为最大的子段和是多少。子段的最小长度为1。
输入输出样例
7 2 -4 3 -1 2 -4 3
4
说明
【样例说明】2 -4 3 -1 2 -4 3
【数据规模与约定】
对于40%的数据,有N ≤ 2000。
对于100%的数据,有N ≤ 200000。
╮(╯▽╰)╭哎 此题脑抽的我为啥非要用分治做呢!!! 窝心ing
此题多解,附上最笨的解法:
(⊙v⊙)嗯 代码:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 const int N=200001; 6 int n,a[N],MAXN; 7 8 int Maxsu(int l,int r) { 9 if(l==r) return a[l]; 10 int mid=(l+r)>>1; 11 int sum1=Maxsu(l,mid); 12 int sum2=Maxsu(mid+1,r); 13 int lmax=-1000000,rmax=-1000000,tot=0; 14 for(int i=mid; i>=l; i--) { 15 tot+=a[i]; 16 if(lmax<tot) lmax=tot; 17 } 18 tot=0; 19 for(int i=mid+1;i<=r;i++){ 20 tot+=a[i]; 21 if(rmax<tot) rmax=tot; 22 } 23 int ans=lmax+rmax; 24 if(ans<=sum1) ans=sum1; 25 if(ans<=sum2) ans=sum2; 26 return ans; 27 } 28 29 int main() { 30 cin>>n; 31 for(int i=1; i<=n; i++) 32 cin>>a[i]; 33 int k=Maxsu(1,n); 34 cout<<k<<endl; 35 return 0; 36 }
分治算法 - 总结
* 分治算法的基本思想是将一个规模为 N 的问题分解为 K 个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。——以上来自百度百科。
* 分治法解题的一般步骤:
1 分解,将要解决的问题划分成若干规模较小的同类问题;
- 二分法:区间对半分开
2 求解,当子问题划分得足够小时,用较简单的方法解决;
- 边界情况:可以直接操作
3 合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
- 合并操作:根据不同的题目来确定
自己选的路,跪着也要走完!