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


首先对数组排序,有序数组相同的数连在一起。之后二分法查找重数数目,二分边界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\n%d\n", 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\n", 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\n",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\n", 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\n", 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\n", ans, cnt); return 0; }

这篇关于《 算法分析与设计》 实验一-分治算法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!