[算法学习笔记]动态规划之钢条切割问题

问题描述

有一个长度为n的钢条需要切割成短钢条出售,长度不同的钢条售价也不同,如下:

长度i12345678910
价格p[i]1589101717202430

那么怎么切割才能获得最大利益呢

暴力解决思路

只要列出每种切割方案,然后比较一下哪种切割方案利润最大不就可以了吗(手动滑稽
假设有一个长度为4的钢条,那么有一下四中切割方案

编号切割方案利润
■■■■9
■■■,■9
■■,■■10
■■,■,■6
■,■,■,■4

发现当钢条长度为4时,切割成两个长度为2的短钢条利润最大。而且当切割钢条时,若切割完成后的钢条长度小于等于1的话,则可以把这个钢条看成规模更小的相同问题,用同样的方法先求出这个短钢条的最大利润,然后比较所有组合的利润,构成原问题的解。
钢条切割问题满足最优子结构的性质:问题的最优解由相关问题的最优解组合而成,而这些子问题可以独立求解。
通过递归的求解方式可以写出简单的求解程序:从钢条的左边切割下一段长度为i的短钢条,然后右边为长度就为n-i的短钢条,然后对右边继续递归求解,知道右边的长度为0,则返回零。

#include <stdio.h>
#define max(a,b) (a>b?a:b)
#define INF 0x7fffff; 

// 不同长度钢条的价格 
int p[11] = {
    0,1,5,8,9,10,17,17,20,24,30
};

int cutROd(int p[], int n){
    int i, q;
    if(n == 0)
        return 0;
    q = -INF;
    for(i = 1; i <= n; i++){
        q = max(q, p[i] + cutROd(p, n - i));
    }

    return q;
}

int main(){
    int n;
    scanf("%d", &n);
    printf("%d\n", cutROd(p, n));

    return 0;
}

指数爆炸

图样图森破啊,这个程序的复杂度是2^n,所以输入大一点的数就会指数爆炸了,等半天结果才会出来,所以这样写太native了

更高效的方法

改进上面的程序

上面的程序之所以低效,因为有很多重复的计算啊,比如说计算长度为4的钢条,便有多次计算了长度为2和3的最优解,更不要说在大一点的数要重复计算多少次了。但是,如果能避免这些重复计算的,算法的效率就会非常高了,只要在第一次计算中吧结果记录下来,下次需要用到的时候直接调用就可以了。

#include <stdio.h>
#define max(a,b) (a>b?a:b)
#define INF 0x7fffff; 

int p[11] = {
    0,1,5,8,9,10,17,17,20,24,30
};

int memoizedCutRodAux(int p[], int n, int r[]){
    int q;
    if(r[n] >= 0)
        return r[n];
    if(n == 0)
        q = 0;
    else{
        q = -INF;
        int i;
        for(i = 1; i <= n; i++)
            q = max(q, p[i] + memoizedCutRodAux(p, n-i, r)) ;
    }
    r[n] = q;
    return q;
}

int memoizedCutRod(int p[], int n){
    int r[11], i;
    for(i = 0; i < 11; i++)
        r[i] = -INF;
    return memoizedCutRodAux(p, n, r);
}

int main(){
    int n;
    scanf("%d", &n);
    printf("%d\n", memoizedCutRod(p, n));

    return 0;
} 

另一种解法

#include <stdio.h>
#define max(a,b) (a>b?a:b)
#define INF 0x7fffff; 

int p[11] = {
    0,1,5,8,9,10,17,17,20,24,30
};

int buttomUpCutRod(int p[], int n){
    int r[n+1];
    r[0] = 0;
    int i, j;
    for(i = 1; i <= n; i++){
        int q = -INF;
        for(j = 1; j <= i; j++)
            q = max(q, p[j] + r[i - j]);
        r[i] = q;
    }

    return r[n];
} 

int main(){
    int n;
    scanf("%d", &n);
    printf("%d\n", buttomUpCutRod(p, n));

    return 0;
} 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值