zoukankan      html  css  js  c++  java
  • 《 算法分析与设计》 实验一-分治算法

     - 众数问题

    Description

    给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。多重集S中重数最大的元素称为众数。例如,S={1,2,2,2,3,5}。多重集S的众数是2,其重数为3。对于给定的由n 个自然数组成的多重集S,计算S的众数及其重数。如果出现多个众数,请输出最小的那个。

    Input

    输入数据的第1行是多重集S中元素个数n(n<1300000);接下来的n行中,每行有一个最多含有5位数字的自然数,。

    Output

    输出数据的第1行给出众数,第2行是重数。

    Sample

    Input 

    6  1 2 2 2 3 5

    Output 

    2
    3

    首先对数组排序,有序数组相同的数连在一起。之后二分法查找重数数目,二分边界i,j意味着a[mid]这个数的数量,用cnt记录。
    如果递归边界l,r小于当前认为的众数数量cnt,说明这个边界里没有数量比它多的,直接返回。
    快排优化记一下
    #include <bits/stdc++.h>
    
    using namespace std;
    int a[1300100];
    int ans = 0, cnt = 0;
    
    void erfen(int l, int r){
        if(r - l + 1 < cnt)
            return;
        int mid = (l+r) >> 1;
        int i = mid, j = mid;
        while(a[mid] == a[i])
            i--;
        while(a[mid] == a[j])
            j++;
        int len = j - i - 1;
        if(cnt < len || (cnt == len && ans > a[mid])){
            ans = a[mid];
            cnt = len;
        }
        erfen(l, i);
        erfen(j, r);
    }
    
    int change(int l, int r){
        int t = rand() % (r - l + 1) + l;
        int temp = a[l];
        a[l] = a[t];
        a[t] = temp;
        t = a[l];
        int i = l, j = r;
        while(i < j){
            while(a[j] >= t && i < j)
                j--;
            a[i] = a[j];
            while(a[i] <= t && i < j)
                i++;
            a[j] = a[i];
        }
        a[i] = t;
        return i;
    }
    
    void quickSort(int l, int r){
        if(l < r){
            int t = change(l, r);
            quickSort(l, t-1);
            quickSort(t+1, r);
        }
    }
    
    
    int main()
    {
        int n;
        scanf("%d", &n);
        for(int i = 0; i<n; i++){
            scanf("%d", &a[i]);
        }
        quickSort(0, n-1);
        erfen(0, n-1);
        printf("%d
    %d
    ", ans, cnt);
        return 0;
    }

    B - 整数因子分解问题

    Description

    大于1的正整数n可以分解为:n=x1*x2*…*xm。例如,当n=12 时,共有8 种不同的分解式:
    12=12;
    12=6*2;
    12=4*3;
    12=3*4;
    12=3*2*2;
    12=2*6;
    12=2*3*2;
    12=2*2*3。
    对于给定的正整数n,计算n共有多少种不同的分解式。

    Input

    输入数据只有一行,有1个正整数n (1≤n≤2000000000)。

    Output

    将计算出的不同的分解式数输出。

    Sample

    Input 

    12

    Output 

    8

    看完题...嗯...想不出来,看完别人的:原来是用这种方法解决的吗...
    K本身代表一个分解方法,sum从1开始,如果k能再分解,次数继续增加。
    诡异的出错点在于...按照题目要求它的最大值好像没有那么大...但用int就会WA

    #include <bits/stdc++.h>
    
    using namespace std;
    
    int a[500000];
    int n;
    
    long long int searchs(int k){
        long long int sum = 1;
        if((k < 500000) && a[k] != 0){
            return a[k];
        }
        for(int i = 2; i<=sqrt(k); i++){
            if(k % i == 0){
                sum += searchs(i);
                if(i * i != k){
                    sum += searchs(k / i);
                }
            }
        }
        if(k < 500000)
            a[k] = sum;
        return sum;
    }
    
    int main()
    {
       scanf("%d", &n);
       printf("%lld
    ", searchs(n));
        return 0;
    }

    C - 顺序表应用7:最大子段和之分治递归法

    Description

    给定n(1<=n<=50000)个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n。 例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。

    注意:本题目要求用分治递归法求解,除了需要输出最大子段和的值之外,还需要输出求得该结果所需的递归调用总次数。

    递归调用总次数的获得,可以参考以下求菲波那切数列的代码段中全局变量count的用法:

    #include
    int count=0;
    int main()
    {
    int n,m;
    int fib(int n);
    scanf("%d",&n);
    m=fib(n);
    printf("%d %d ",m,count);
    return 0;
    }
    int fib(int n)
    {
    int s;
    count++;
    if((n==1)||(n==0)) return 1;
    else s=fib(n-1)+fib(n-2);
    return s;
    }

    Input

    第一行输入整数n(1<=n<=50000),表示整数序列中的数据元素个数;

    第二行依次输入n个整数,对应顺序表中存放的每个数据元素值。

    Output

    一行输出两个整数,之间以空格间隔输出:

    第一个整数为所求的最大子段和;

    第二个整数为用分治递归法求解最大子段和时,递归函数被调用的总次数。

    Sample

    Input 

    6
    -2 11 -4 13 -5 -2

    Output 

    20 11

    Hint

    感觉动态规划做这题比分治要好理解多了...但是不行,还要递归次数

    这个二分看了好一会为什么要这么整。分段之后得不到两段共同拥有的中间部分的加和,所以还得手动求

    比如样例里这个,最大和子段应该是11+13-4=20,左段为(-2,11,-4),右段为(13,-5,-2),左段求得11,右端求得13,从中间值mid开始向左和向右求和,找出连接左右段的最大和(-4+11)+(13) = 20;

    #include <bits/stdc++.h>
    
    using namespace std;
    int a[50010];
    int ans = 0, cnt = 0;
    
    int erfen(int l, int r){
        cnt++;          //统计重数
        if(l == r){
            if(a[l] > 0)
                return a[l];
            else
                return 0;
        }
        int mid = (l + r) >> 1;
        int s1 = 0, s2 = 0, s = 0;
        for(int i = mid; i >= l; i--){
            s += a[i];
            if (s > s1)
                s1 = s;
        }
        s = 0;
        for(int i = mid+1; i <= r; i++){
            s += a[i];
            if (s > s2)
                s2 = s;
        }
    
        int maxl = erfen(l, mid);
        int maxr = erfen(mid+1, r);
        maxl = max(maxl, maxr);
        if(maxl > s1 + s2)
            return maxl;
        else
            return s1 + s2;
    }
    
    int main()
    {
        int n;
        scanf("%d", &n);
        for(int i = 0; i<n; i++){
            scanf("%d", &a[i]);
        }
        ans = erfen(0, n-1);
        printf("%d %d
    ", ans, cnt);
        return 0;
    }

    D - 骨牌铺方格

    Description

    在2×n的一个长方形方格中,用一个1× 2的骨牌铺满方格,输入n ,输出铺放方案的总数. 例如n=3时,为2× 3方格,骨牌的铺放方案有三种,如下图:

    Input

    输入包含一个整数n,表示该测试实例的长方形方格的规格是2×n (0< n<=50)。

    Output

    输出铺放方案的总数。

    Sample

    Input 

    3

    Output 

    3
    跟走楼梯一样,当你想要使用n个骨牌时,你可以从之前只用了n-1个骨牌的方块中补上一块,也可以从之前的缺两块的方格中补上两块。(拼一块和两块的方法数目已知)
    不过这也是分治吗...?

    #include <bits/stdc++.h>
    
    using namespace std;
    
    long long int a[55];
    
    int main()
    {
        int n;
        scanf("%d", &n);
        a[1] = 1;
        a[2] = 2;
        for(int i = 3; i<=n; i++){
            a[i] = a[i-1] + a[i-2];
        }
        printf("%lld
    ", a[n]);
        return 0;
    }
     

    #include <bits/stdc++.h> using namespace std; int a[50010]; int ans = 0, cnt = 0; int erfen(int l, int r){ cnt++; //统计重数 if(l == r){ if(a[l] > 0) return a[l]; else return 0; } int mid = (l + r) >> 1; int s1 = 0, s2 = 0, s = 0; for(int i = mid; i >= l; i--){ s += a[i]; if (s > s1) s1 = s; } s = 0; for(int i = mid+1; i <= r; i++){ s += a[i]; if (s > s2) s2 = s; } int maxl = erfen(l, mid); int maxr = erfen(mid+1, r); maxl = max(maxl, maxr); if(maxl > s1 + s2) return maxl; else return s1 + s2; } int main() { int n; scanf("%d", &n); for(int i = 0; i<n; i++){ scanf("%d", &a[i]); } ans = erfen(0, n-1); printf("%d %d ", ans, cnt); return 0; }

  • 相关阅读:
    为什么要选择忍者站群?
    SDCMS1.31 如何发布?
    强大的忍者站群
    出现未能加载“OpenWebKitSharp”是什么原因?
    wordpress发布模块如何使用?
    忍者站群做了百度竞价,岂不是跟站群相违背吗?
    点点博客的发布模块,如何使用?
    为什么有时候明明提示登陆成功,却无法获取分类或无法发布?
    被百度K了,怎么办?
    笔记0611
  • 原文地址:https://www.cnblogs.com/kaito77/p/15316922.html
Copyright © 2011-2022 走看看