分治算法小总结 x

分治算法的基本思想是将一个规模为 N 的问题分解为 K 个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。

                  ——以上来自百度百科。

* 分治法解题的一般步骤:
1 分解,将要解决的问题划分成若干规模较小的同类问题;
- 二分法:区间对半分开
2 求解,当子问题划分得足够小时,用较简单的方法解决;
- 边界情况:可以直接操作
3 合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
- 合并操作:根据不同的题目来确定

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

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

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。

思路:

     归并过程为:比较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]。对左右子区间的排序与原问题一样,所以我们可以调用同样的子程序,只是区间大小不一样。

代码酱来也~

#include<iostream>
#include<algorithm>
#define M 111111

using namespace std;

int n,ans;
int a[M],b[M];

void merge_sort(int l,int r)
{
    if(l == r) return;

    int m = (l+r) / 2 ;//中间值

    merge_sort(l,m);//左边
    merge_sort(m+1,r); //右边

    int i=l,j=m+1,k=l;

    for(;i<=m&&j<=r;)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else
        {
            ans+=m-i+1;//统计产生逆序对的个数
            b[k++]=a[j++];
        }
    }

    for(;i<=m;b[k++]=a[i++]);
    for(;j<=r;b[k++]=a[j++]);

    for(int i=l;i<=r;i++)
    a[i]=b[i];
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];

    merge_sort(1,n);

    cout<<ans;

    return 0;
}

/*----------------------分割线---------------------*/
 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

思路:

  这题有毒!!!where is the 数据范围???

这数据范围太大了有没有!!!所以int类型的就会炸,将int改为long long就ok了!

这题我交了

代码酱来也~

#include<iostream>
#include<algorithm>
#define M 111111
#define LL long long

using namespace std;

LL ans;
int n;
int a[M],b[M];

void merge_sort(int l,int r)
{
    if(l == r) return;

    int m = (l+r) / 2 ;//中间值

    merge_sort(l,m);//左边
    merge_sort(m+1,r); //右边

    int i=l,j=m+1,k=l;

    for(;i<=m&&j<=r;)
    {
        if(a[i]<=a[j]) b[k++]=a[i++];
        else
        {
            ans+=(LL)m-(LL)i+1; //统计产生逆序对的个数
            b[k++]=a[j++];
        }
    }

    for(;i<=m;b[k++]=a[i++]);
    for(;j<=r;b[k++]=a[j++]);

    for(int i=l;i<=r;i++)
    a[i]=b[i];
}

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];

    merge_sort(1,n);

    cout<<ans;

    return 0;
}

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。

思路:

  这道题真的就是一道模板题,但是为什么我交了好几遍就是没有AC呢?原因出在第二个点上,因为第二个点里的数据似乎全部都是负的,所以在进行初始化的时候需要赋值为一个极小数(ans,lmax,rmax这三个),不然按一般的话,一定是从0开始,所以负数就出不来,所以……

代码酱来也~

#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long 

using namespace std;

const int N = 2e5 + 6;

int n;
int a[N];

LL calc(int l, int r)
{
    if(l==r) return a[l];

    int m = (l+r) / 2;
    LL ret = max(calc(l, m), calc(m+1, r));

    int lmax = -1000000, rmax = -1000000;
    for(int i=m, s=0; i>=l; i--) s+=a[i], lmax=max(lmax, s);
    for(int i=m+1, s=0; i<=r; i++) s+=a[i], rmax=max(rmax, s);

    ret = max(ret, (LL)(lmax) + (LL)(rmax));
    return ret;
}

int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);

    LL ans=-1000000;
    ans = max(ans, (LL)calc(1, n));
    cout<<ans;

    return 0;
}

5.洛谷P1010 幂次方

题目描述

任何一个正整数都可以用2的幂次方表示。例如

    137=2^7+2^3+2^0         

同时约定方次用括号来表示,即a^b 可表示为a(b)。

由此可知,137可表示为:

    2(7)+2(3)+2(0)

进一步:7= 2^2+2+2^0 (2^1用2表示)

    3=2+2^0   

所以最后137可表示为:

    2(2(2)+2+2(0))+2(2+2(0))+2(0)

又如:

    1315=2^10 +2^8 +2^5 +2+1

所以1315最后可表示为:

    2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

输入输出格式

输入格式:

一个正整数n(n≤20000)。

输出格式:

符合约定的n的0,2表示(在表示中不能有空格)

输入输出样例

输入样例#1:

1315

输出样例#1:

2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0)

思路:  如果遇到2或者1,特判一下,直接输出,其余的如果能够继续分解,就将其分解(分治~)代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

using namespace std;

void work(int n)
{
    if(n==1)
    {
        printf("2(0)");
        return;
    }
    if(n==2)
    {
        printf("2");
        return;
    }
    else
    {
        int j=1,i=0;
        do
        {
            j*=2;
            if(j>n)///超出该数n
            {
                j/=2;
                if(i==1) printf("2");///特判
                else
                {
                    printf("2(");///输出形式
                    work(i);
                    printf(")");
                }
                if(n-j!=0)///若还能够继续分解
                {
                    printf("+");///用+连接
                    work(n-j);///继续分解
                }
                return;
            }
            else i++;///如果还不够大,继续加
        }while(1);
    }
}

int main()
{
    int n;
    cin>>n;
    work(n);
    return 0;
}

				
时间: 2024-12-26 05:04:52

分治算法小总结 x的相关文章

分治算法(Divide and Conquer)

分治算法 在计算机科学中,分治法是建基于多项分支递归的一种很重要的算法范式.字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并. 分治法所能解决的问题一般具有以下几个特征: 问题的规模缩小到一定的程度就可以容易地解决 问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质 利用该问题分解出的子问题的解可以合并为该问题的解 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问

分治算法(二)

大家都知道选择排序和冒泡排序,这两个排序都是双重for循环,时间复杂度为O(n^2),显然效率都是比较低的,而运用分治思想的归并排序和快速排序会更高效一些. 1.归并排序 1)原理:假设初始序列含有n个记录,则可以看成是n个有序子序列,每个子序列的长度为1,然后两两归并,得到[n/2]([x]表示不小于x的最小整数)个长度为2或1的有序子序列:再两两归并,--,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法成为2路归并排序.(摘自<大话数据结构>) 可见其运用了典型的分治思想,①分

基于分治算法的归并排序

#include <stdio.h> #include <math.h> void main() { int array[] = {1,212,35,1,456,12376,167,12,7523,71,634}; mergeSort(array, 0, 10); for(int i = 0; i < 11; i++ ) { printf("%d\n", array[i]); } } void mergeSort(int* array, int start

五大算法之分治算法

一.基本思想 当我们求解某些问题时,由于这些问题要处理的数据相当多,或求解过程相当复杂,使得直接求解法在时间上相当长,或者根本无法直接求出.对于这类问题,我们往往先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法.如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止.这就是分治策略的基本思想. 二.二分法 利用分治策略求解时,所需时间取决于分解后子问题的个数.子问题的规模大小等因素,而二分法,由于其划

再回首--分治算法

谈起分治算法,首先从字面意思理解:就是将一个问题划分成多个较小的问题的算法.其实正应题目的意思.其基本设计思想就是:将一个难以直接解决的大问题分解成一些规模较小的相同问题以便各个击破,分而治之. 设计步骤:1)分解:分解成若干子问题 2)求解:求解个子问题 3)合并:将子解合并成原问题的解. 在自考的时候,我们遇到的二路归并算法就属于一种分治法.当然,要学会算法,就要找到其核心,抓住其核心了,我们也就明白算法是怎么回事了.下面我们通过二路归并算法找到其核心. 例子:给出一列数:4,2,8,3.利

分治算法(一)

当我们求解某些问题时,由于这些问题要处理的数据相当多,或求解过程相当复杂,使得直接求解法在时间上相当长,或者根本无法直接求出.对于这类问题,我们往往先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法.如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止.这就是分治策略的基本思想. 1.引例: 如果给你一个装有16枚硬币的袋子,其中有一枚是伪造的,并且那枚伪造硬币的重量和真硬币的重量不同.你能不能用最少

【数据结构】分治算法求解假硬币问题

问题描述: 概念 分治算法的基本思想是将一个大的复杂的问题分解成多个小的.容易解决的问题,通过解决这些小问题进而解决这个大问题. 使用分治算法需要待求解问题能够简化为若干个小规模的相同的问题,通过逐步划分,达到一个易于求解的阶段,而直接进行求解,在程序中可以使用递归方法来进行求解. 哈哈,说起来很抽象,举个例子就好理解了. 一个袋子里有n个硬币,其中一枚是假币,并且假币和真币一模一样,仅凭肉眼无法区分,仅知道假币比真币轻一些,请问如何查找到假币? 分治算法: 我们可以这样做: 将这n个硬币分成两

分治算法(思想)在数据结构中的应用

分治算法: 简单的概括就是将暂时不能解决的大问题分成许多入门的子问题, 如果子问题还是不能解决的话则继续分成子问题,直到子问题小到 可以解决为止的规模,原问题即是子问题的合并. PartOne: 用分治法打印数组a[L,......,R]. 分析: 一个循环就可以了,但是分治算法来解决该怎么做呢? 如果待打印的序列长度为 1,则可以直接打印; 如果待打印的序列长度为 N,则可将其划分为两部分; 第一部分是 1, 后N - 1 是另一个划分,以此类推,直到数组长度是 1. void print(i

【算法】2 由股票收益问题再看分治算法和递归式

回顾分治算法 分治算法的英文名叫做"divide and conquer",它的意思是将一块领土分解为若干块小部分,然后一块块的占领征服,让它们彼此异化.这就是英国人的军事策略,但我们今天要看的是算法. 如前所述,分治算法有3步,在上一篇中已有介绍,它们对应的英文名分别是:divide.conquer.combine. 接下来我们通过多个小算法来深化对分治算法的理解. 二分查找算法 问题描述:在已排序的数组A中查找是否存在数字n. 1)分:取得数组A中的中间数,并将其与n比较 2)治: