Solution to CLRS Chapter 4

这一章主要介绍了几个divide-and-conquer的例子,并且引入了对递归算法的复杂度进行刻画的数学工具,这章工具数学性很强,所以数学部分打算以后遇到了再回头来看,不过那个master theorem真的好方便啊!

4.1-1
用divide-and-conquer处理最大连续子序列和,在conquer的时候,涉及3个sum之间的比较:
left-sum,right-sum,cross-sum,其中cross-sum必须要包含a[mid],a[mid+1]两个点,由于整个序列都是负数,所以cross-sum是最小的(加的越多越下),先排除,这样,剩下left-sum和right-sum本质上是就是找左右区间中最大的那个元素,所以,最后的结果一定返回的是[1…n]中,最大的负数

4.1-2
brute-force算法,就是要枚举每一种买股票的可能,这可以通过枚举买进时间i,卖出时间j来实现,这样的复杂度,已经达到theta(n^2)了,所以,在确定了区间[i,j]的时候,需要一个常数级算法,求出区间[i,j]的和,这个可以用一个预处理,得到一个sum[]数组,sum[i]表示前i项的和,那么区间[i,j]的和就可以表示成sum[j]-sum[i-1]
pseudocode:

BRUTE-FORCE-MAXIMUN-SUBARRAY
        sum[0] = 0;
        for i = 1 : N
                sum[i] = sum[i-1] + a[i]
        maxSum = -INF
        for i = 1 : N
                for j = i : N
                        if sum[j] - sum[i-1] > maxSum
                                maxSum = sum[j] - sum[i-1]
                                startPoint = i;
                                endPoint = j;
        return (maxSum, startPoint, endPoint)

4.1-3
这题是判断brute-force算法和用divide-and-conquer paradigm的性能的分界点,然后用这个来优化recursive版本算法的性能,换角度说也就是coarsen the leaves,这个思想在之前的merge sort 也用到了。
下面用C++实现一下这个比较:(比较方法是测试规模为10000的数据)
我电脑测试的时候,从n=1开始,就是divide-and-conquer更加优秀………………
C++ code:

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <cstdio>

using namespace std;

const int INF = 0x7fffffff;
const int MAX_N = 1000000 + 3;

int A[MAX_N];

int CrossSum (int *A, int l, int mid, int r) {
        int left_max = -INF, left_sum = 0;
        int right_max = -INF, right_sum = 0;
        for (int i = mid; i >= l; --i) {
                left_sum += A[i];
                left_max = max(left_max, left_sum);
        }
        for (int i = mid+1; i <= r; ++i) {
                right_sum += A[i];
                right_max = max(right_max, right_sum);
        }
        return left_max + right_max;
}

int RecursiveMaxSub (int *A, int l, int r) {
        if (l == r) 
                return A[l];
        int mid = (l + r) / 2;
        int left_sum = RecursiveMaxSub(A, l, mid);
        int right_sum = RecursiveMaxSub(A, mid+1, r);
        int cross_sum = CrossSum(A, l, mid, r);
        int maxSum = left_sum;
        if (right_sum > maxSum)
                maxSum = right_sum;
        if (cross_sum > maxSum)
                maxSum = cross_sum;
        return maxSum;
}

int sum[MAX_N];
int BruteForceMaxSub (int *A, int N) {
        int i, j, maxSum = -INF;
        sum[0] = 0;
        for (i = 1; i <= N; ++i)
                sum[i] = sum[i-1] + A[i];
        for (i = 1; i <= N; ++i)
                for (j = i; j <= N; ++j)
                        if (sum[j] - sum[i-1] > maxSum)
                                maxSum = sum[j] - sum[i-1];
        return maxSum;
}

void getNewArray(int* A, int n) {
        for (int i = 1; i <= n; ++i)
                A[i] = rand() % 1000 - 500;
}

int main (int argc, char* argv[]) {
        int n = 9;       

        srand(time(NULL));
        getNewArray(A, n); //randomly get a new array

        int time1 = clock();
        int result1 = BruteForceMaxSub(A, n);
        time1 = clock() - time1;

        int time2 = clock();
        int result2 = RecursiveMaxSub(A, 1, n);
        time2 = clock() - time2;

        if (result1 != result2)
                cout << "Algorithm Error!" << endl;
        //cout << "brute-force: " << time1 / CLOCKS_PER_SEC << " s   divide-and-conquer: " << time2 / CLOCKS_PER_SEC << " s" << endl;

        cout << "brute-force: " << time1 << " clocks   divide-and-conquer: " << time2 << " clocks" << endl;
        return 0;
}

4.1-4
这个挺简单的,不允许empty array只会出现在所有的元素都是负数的情况下,由4.1-1知这种情况下,返回的一定是元素中最大的负数,所以,只要加一个判断-如果MaxSub返回的结果是负数,那么显然这个结果不如选取一个空串得到0,所以将所以返回负数的情况,特判改成0就可以了。

4.1-5
这道题就是动态规划的思想吧,hdu1003 Max Sum 就是这个题目,还有一个升级版是hdu1024
这里给出升级版:

#include <cstdio>
#include <cstring>
#include <algorithm>
using std::max;

const int MAX_N = 1000000 + 6;
const int INF = 0x7fffffff;

int dp[MAX_N];
int a[MAX_N];
int rmax[MAX_N];

int main (int argc, char* argv[]) {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif


    int n, m;
    while (scanf("%d%d", &m, &n) != EOF) {
        for (int i = 1; i <= n; ++i)
            scanf("%d", a + i);
        memset(dp, 0, sizeof(dp));
        memset(rmax, 0, sizeof(rmax));
        int ans = -INF;
        int i, j;
        for (i = 1; i <= m; ++i) {
            ans = -INF;
            for (j = i; j <= n; ++j) {
                dp[j] = max(dp[j-1], rmax[j-1]) + a[j];
                rmax[j-1] = ans;
                ans = max(ans, dp[j]);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

这一章有点奇怪,但感觉并不是无用,回头再看
4.2-1
4.2-2
4.2-3
4.2-4
4.2-5
4.2-6
4.2-7

以后遇到相关的计算再来看,现在对这个没什么概念

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值