钢条切割——算法导论

自顶向下解法

分解:将长度为n的钢条切割问题转为在i下标切割后长度为n-1的子问题
解决:递归求解子问题,每次长度参数-1,在n=0即不用切时递归终止
合并:
1. 每个子问题的解+前一段切割长度的arr[i]值 成为前一子问题的最优解
2. 原规模问题中的最优解是所有子问题解中值最大的

//自顶向下
int max_sum(int arr[], int n)
{
    if (n == 0) {
        return 0;
    }
    int i = 0;
    int maxn = INT_MIN;
    int sum = 0;
    //边界,最后一次切割点是arr[n-1]+0
    for (i = 1; i <= n; i++) {
        sum = arr[i-1] + max_sum(arr, n - i);
        if (maxn < sum) {
            maxn = sum;
        }
    }
    return maxn;
}

为什么慢?
反复使用相同参数对自身进行递归调用,反复求解相同子问题。O(2^n)

带备忘的自顶向下

仍按自顶向下的方法编写,但过程中保存每个子问题的解。
敲重点:在过程中保存子问题解,在每次递归之前检查是否保存了解。

//带备忘的自顶向下
#define N  100
int maxArr[N];
void init(int maxArr[N])
{
    for (int i = 0; i < N; i++) {
        maxArr[i] = INT_MIN;
    }
}
int m_max_sum(int arr[], int n)
{   
    //检查
    if (maxArr[n] > INT_MIN) {
        return maxArr[n];
    }
    if (n == 0) {
        return 0;
    }
    int i = 0;
    int maxn = INT_MIN;
    int sum = 0;
    for (i = 1; i <= n; i++) {
        sum = arr[i - 1] + max_sum(arr, n - i);
        if (maxn < sum) {
            maxn = sum;
        }
    }
    //保存
    maxArr[n] = maxn;
    return maxn;
}

自底向上

这种方法一般需要恰当定义子问题“规模”,使得任何子问题的求解都只依赖于“更小的”子问题的求解。因此我们将子问题按规模从小到大进行求解。当求解某个子问题时,它所依赖的更小子问题已经求解完毕,结果已经保存,所以每个子问题只求解一次,当我们求解它时,它的所有前提子问题都已求解完成。
以上是我手打的书上原话,每一句都很精辟,字字珠玑。

//自底向上
int btm_up(int arr[], int n)
{
    int j = 0, i = 0;
    int maxnn = INT_MIN;
    int sum = 0;
    //最小规模的解
    maxArr[0] = 0;
    //规模为j的子问题
    for (j = 1; j <= n; j++) {
        //求解规模为i的子问题与原来方法一样,只是规模i < j,所以在求规模j的时候不需要递归
        for (i = 1; i <= j; i++) {
            sum = arr[i - 1] + maxArr[j - i];
            if (sum > maxnn) {
                maxnn = sum;
            }
        }
        maxArr[j] = maxnn;
    }
    return maxArr[n];
}

重构解

对每个子问题不仅保存最优值,还保存切割点,利用这些信息输出最优解。

//最后pos[n]就是最优的切割点
int pos[N] = { 0 };
//自底向上重构解
int btm_up_op(int arr[], int n)
{
    int j = 0, i = 0;
    int maxnn = INT_MIN;
    int sum = 0;
    //最小规模的解
    maxArr[0] = 0;
    //规模为j的子问题
    for (j = 1; j <= n; j++) {
        //在每个j规模下,maxnn是最优价格,pos[j]存的是前一段最优切割长度
        for (i = 1; i <= j; i++) {
            sum = arr[i - 1] + maxArr[j - i];
            if (sum > maxnn) {
                maxnn = sum;
                pos[j] = i;
            }
        }
        maxArr[j] = maxnn;
    }
    return maxArr[n];
}

猜你喜欢

转载自blog.csdn.net/hanzheng6602/article/details/80091250