• 分治算法


    归并排序

    现在对于排序问题用一个sort就欧了,根本就没考虑归并排序这个东东,正好做分治的题目,而归并排序又有分治的思想,所以做了两个水题。。。

    其实这个题用冒泡排序做的,但用归并排序也能做出来(分析一下此题与逆序对是有相同之处的)

    由于两者的代码完全一样,就只放一个啦  ~(≧▽≦)/~啦啦啦

    1.洛谷 P1116 车厢重组

    题目描述

    在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转。一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转180度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排列车厢的顺序。于是他就负责用这座桥将进站的车厢按车厢号从小到大排列。他退休后,火车站决定将这一工作自动化,其中一项重要的工作是编一个程序,输入初始的车厢顺序,计算最少用多少步就能将车厢排序。

    输入输出格式

    输入格式:

    输入文件有两行数据,第一行是车厢总数N(不大于10000),第二行是N个不同的数表示初始的车厢顺序。

    输出格式:

    一个数据,是最少的旋转次数。

    输入输出样例

    输入样例#1:
    4
    4 3 2 1 
    输出样例#1:
    6
    /*----------------------分割线---------------------*/
    2.洛谷 P1908 逆序对

    题目描述

    猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。

    输入输出格式

     输入格式:

    第一行,一个数n,表示序列中有n个数。

    第二行n个数,表示给定的序列。

     输出格式:

    给定序列中逆序对的数目。

     输入输出样例

     输入样例#1:
    6
    5 4 2 6 3 1
    
     输出样例#1:
    11

    说明

    对于50%的数据,n≤2500

    对于100%的数据,n≤40000。

    思路:
    其实思路非常简单,如果用枚举法的话,在数据范围小的时候是可以的,但是某些没良心的出题人,就是不让你简单的拿分啊~ ( ⊙ o ⊙ )啊!
         归并过程为:比较A[i]和A[j]的大小,若A[i]≤A[j],则将第一个有序表中的元素A[i]复制到R[k]中,并令i和k分别加1,即使之分别指问后一单元,否则将第二个有序表中的元素A[j]复制到R[k]中,并令j和k分别加1;如此循环下去,直到其中的一个有序表取完,然后再将另一个有序表中剩余的元素复制到R中从下标k到下标t的单元.
         归并排序算法我们用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。对左右子区间的排序与原问题一样,所以我们可以调用同样的子程序,只是区间大小不一样。
     

     (⊙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 求逆序对
     时间限制: 1 s
     空间限制: 128000 KB
     题目等级 : 黄金 Gold
     
    题目描述 Description

    给定一个序列a1,a2,…,an,如果存在i<j并且ai>aj,那么我们称之为逆序对,求逆序对的数目

    数据范围:N<=105。Ai<=105。时间限制为1s。

    输入描述 Input Description

    第一行为n,表示序列长度,接下来的n行,第i+1行表示序列中的第i个数。

    输出描述 Output Description

    所有逆序对总数.

    样例输入 Sample Input

    4

    3

    2

    3

    2

    样例输出 Sample Output

    3

    数据范围及提示 Data Size & Hint
     
          哼哼!!由于某些原因数据范围居然不见了。。。。。。。
       数据范围太大啦!!!所以int类型的就会炸,所以将int改为long long就阔以了!
    (⊙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。

    输入输出样例

    输入样例#1:
    7
    2 -4 3 -1 2 -4 3
    输出样例#1:
    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 合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
    - 合并操作:根据不同的题目来确定

    
    

     自己选的路,跪着也要走完!

  • 相关阅读:
    C#---将数据库数据转换为json格式
    ASP.NET ---根据值让树中某一节点选中
    SQL---查询树中某个节点及其所有子节点
    CSS---相对定位笔记
    CSS---绝对定位笔记
    滑雪
    Self Numbers
    Lotto
    Parencodings
    Robot Motion
  • 原文地址:https://www.cnblogs.com/wsdestdq/p/6820763.html
Copyright © 2020-2023  润新知