剑指offer-14题

前两天有点小事情,所以就断更了两天,其实还是懒。

今天来继续刷刷剑指offer吧。

今天需要用到的知识点是动态规划跟贪心算法。

首先能用动态规划来解决的问题一般有如下四个特点:

1.问题的目的是求最优解。

2.问题可以被拆分位若干个小问题,整体的最优解依赖其子问题的最优解,也可以说整体最优解可以由子问题的最优解组合得来。

3.大问题可以被拆分为若干个子问题,子问题之间还有重叠的子问题。

4.由于有共同的子问题,那么在求大问题最优解的时候可以从上往下分析,求解的时候可以从下往上求解,可以复用子问题的解,避免重复计算。

问题描述:

动态规划&&贪心解法:

public class Design {
    // 动态规划
    public static int findMaxAreaByCutCord(int len) {
        if (len < 2) {
            return 0;
        }
        if (len == 2) {
            return 1;
        }
        if (len == 3) {
            return 2;
        }
        int p[] = new int[len + 1];
        p[0] = 0;
        p[1] = 1;
        p[2] = 2;
        p[3] = 3;
        for (int i = 4; i <= len; i++) {
            for (int j = 1; j <= i / 2; j++) {
                if (p[j] * p[i - j] > p[i]) {
                    p[i] = p[j] * p[i - j];
                }
            }
        }
        return p[len];
    }

    // 贪心
    public static int findMaxAreaByCutCordTx(int len) {
        if (len < 2) {
            return 0;
        }
        if (len == 2) {
            return 1;
        }
        if (len == 3) {
            return 2;
        }
        int cp = len;
        int res = 1;
        while (cp >= 5) {
            res *= 3;
            cp -= 3;
        }
        if (cp == 0) {
            cp = 1;
        }
        return res * cp;
    }

    public static void main(String[] args) {
        System.out.println(findMaxAreaByCutCord(16));
        System.out.println(findMaxAreaByCutCordTx(16));
    }
}

动态规划解法解析:

p数组用来记录动态规划整个计算过程中的子问题的解和大问题的解。

首先初始化了四个值,分别是p[0],p[1],p[2],p[3]p数组代表了子问题的解,p[3]代表了绳子长度为3的子问题的最优值就是3,这里不用对长度为3的绳子再进行切割了,因为作为子问题,可以不切,前提是绳子的长度大于3,p[3]代表是已经被切割过为长度为3的子问题的绳子的最优解。

那么看到这里,你会不会有疑问?

为什么要初始化这三个值,为什么不再把p[4]的值也预先定义了?

前面说的,动态规划的第二个特点中说到,大问题的最优解依赖于其子问题的最优解,那么记该问题的最优解为p[i],当i>3时,是不是p[i]的最优解就可以用p[x]*p[i-x]来求解呢?

其实也很简单,就列个式子:

i <= (i/2)^2         

解得i>=4

式子中的i表示绳子的长度,i/2表示对半切,从i=4开始起,上面这个式子就满足条件了,真实情况下,还要区分奇偶,可以再区分奇偶证明一下,这里就不再证明为奇数的情况了。

所以从i=4开始,绳子就可以进行切割了,即如果问题一开始绳子的长度大于3,比如恰巧等于4,那么可以用p[2]*p[2]来表示。

如果绳子的长度大于4,那么p[4]就是一个子问题,这个时候有两种情况:

1.直接用子问题中绳子的长度(4)来表示。

2.用p[4]的子问题来求最优解。

前面的式子也证明了,当i>3时,可以采用第2种方法来求解的。

所以其实当i>3时,p[i]的解就可以用上面第二种方式来计算了,所以只需要预定义p[0],p[1],p[2],p[3]

 

对于贪心解法,其实也容易想到:

当一根绳子长度大于5的时候,绳子可以被分为2和3组成的短绳子,也就是子问题。

假设绳子长度为5,那么可以拆分为2和3,假设此时问题最优解是p[5]

那么当绳子的长度为6的时候,假设p[6] = p[5]*p[1],这时候其实不合适了,相当于p[6]=p[5],那肯定不可能呀。

所以当绳子的长度为6的时候,可能拆分的子问题的绳子的长度有一根至少为2才会更优,那么又可以拆分为2 2 3 的组合。

依此类推。

 

另外呢,就是书上的证明了:

n>=5时,2*(n-2) >n3(n-3)>n,即当绳子的长度大于5的时候,我们可以把绳子拆分为2或者3的绳子段。

然后,书上又证明了:

n>=5时,3(n-3)>=2(n-2),那就尽量多来一些长度为3的绳子段吧。

另外:假设绳子长度为L,那么Lmod3=e,假设e可能为1或者2,如果为2的话就很普通,直接按书上的方法算好了。如果为1的时候,说明将L拆分为子问题的乘数因子中包含4,那么p[4]=p[2]*p[2],做个特殊区分即可。

ps:上面我写的贪心法跟书上的不一样,书上的更优,我的解释也是解释书上的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值