剑指offer之剪绳子

1.问题描述

题目:给你一根长度为n绳子,请把绳子剪成m段(m、n都是整数,n>1并且m≥1)。每段的绳子的长度记为k[0]、k[1]、……、k[m]。k[0]k[1]…*k[m]可能的最大乘积是多少?例如当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到最大的乘积18。

2.分析

  1. 方法一,动态规划,可以自顶向下,也可以从小到上
  2. 方法二,贪婪算法,我们尽可能多的剪长度为3的绳子,最后长度为4的时候,分成2段长度为2的绳子。

3.源代码

A.从顶到下,动态规划

#include <iostream>
#include <cmath>
#include <vector>
#include <bitset>
using namespace std;
//f(n) = max(f(i)*f(n-i)),1<=i<=n;
long long maxCutting(vector<long long>& products,int length)
{
    //递归边界
    long long  maxLength = products[length];
    if(maxLength!=-1)
        return maxLength;

    //找出最大值

    for(int i=1;i<=length/2;++i)
    {
        long long product = maxCutting(products,i)*maxCutting(products,length-i);
        if(maxLength < product)
            maxLength = product;
    }
    products[length] = maxLength;

    return maxLength;
}

//从顶到下,动态规划
long long maxCutting_solution2(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    vector<long long> products(length+1,-1);
    products[0] = 0;
    products[1] = 1;
    products[2] = 2;
    products[3] = 3;

    return maxCutting(products,length);
}

B.从下到上,动态规划

long long maxCutting_solution1(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    vector<long long> products(length+1,-1);
    products[0] = 0;
    products[1] = 1;
    products[2] = 2;
    products[3] = 3;

    //f(n) = max(f(i)*f(n-i));
    long long maxLength = 0;
    for(int n = 4;n <= length;++n)
    {
        maxLength = products[n];
        //里层循环,找出最大值
        for(int i = 1;i <= n/2;++i)
        {
            long long product = products[i]*products[n-i];
            if(maxLength < product)
                maxLength = product;
        }
        products[n] = maxLength;
    }

    return products[length];
}

C.贪婪算法

long long myPow(int number,int times)
{
    long long product = 1;
    for(int i=0;i !=times;++i)
    {
        product *=number;
    }

    return product;
}
//贪婪算法
long long maxCutting_solution3(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    int threeOfTimes = length/3;
    if(length - 3*threeOfTimes==1)
    {
        threeOfTimes -=1;
    }
    int twoOfTimes = (length-3*threeOfTimes)/2;


    return myPow(3,threeOfTimes)*myPow(2,twoOfTimes);
}

D.完整代码

//从下到上,动态规划
long long maxCutting_solution1(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    vector<long long> products(length+1,-1);
    products[0] = 0;
    products[1] = 1;
    products[2] = 2;
    products[3] = 3;

    //f(n) = max(f(i)*f(n-i));
    long long maxLength = 0;
    for(int n = 4;n <= length;++n)
    {
        maxLength = products[n];
        for(int i = 1;i <= n/2;++i)
        {
            long long product = products[i]*products[n-i];
            if(maxLength < product)
                maxLength = product;
        }
        products[n] = maxLength;
    }

    return products[length];
}
//f(n) = max(f(i)*f(n-i)),1<=i<=n;
long long maxCutting(vector<long long>& products,int length)
{
    //递归边界
    long long  maxLength = products[length];
    if(maxLength!=-1)
        return maxLength;

    //找出最大值

    for(int i=1;i<=length/2;++i)
    {
        long long product = maxCutting(products,i)*maxCutting(products,length-i);
        if(maxLength < product)
            maxLength = product;
    }
    products[length] = maxLength;

    return maxLength;
}

//从顶到下,动态规划
long long maxCutting_solution2(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    vector<long long> products(length+1,-1);
    products[0] = 0;
    products[1] = 1;
    products[2] = 2;
    products[3] = 3;

    return maxCutting(products,length);
}
long long myPow(int number,int times)
{
    long long product = 1;
    for(int i=0;i !=times;++i)
    {
        product *=number;
    }

    return product;
}
//贪婪算法
long long maxCutting_solution3(int length)
{
    if(length < 2)
        return 0;
    if(length == 2)
        return 1;
    if(length == 3)
        return 2;

    int threeOfTimes = length/3;
    if(length - 3*threeOfTimes==1)
    {
        threeOfTimes -=1;
    }
    int twoOfTimes = (length-3*threeOfTimes)/2;


    return myPow(3,threeOfTimes)*myPow(2,twoOfTimes);
}
void Test(int length)
{
    cout <<"length:"<<length<<endl;
    cout <<maxCutting_solution1(length)<<":"<<maxCutting_solution2(length)<<":"<<maxCutting_solution3(length)<<endl;
}

int main()
{
    for(int length = 50;length <=80;length+=2)
       Test(length);
    return 0;
}

运行结果图:
这里写图片描述

4.总结

这是动态规划的好例子,可以使用自下向上的动态规划,这个和菲波那切数列有相似点,难点在于绳子长度为1,2,3时候,需要单独讨论,当n>=4之后,就可以使用f(n)=max(f(i)*f(n-i))
使用贪婪算法,当绳子最后剩下的长度为4的时候,不能再剪去长度为3的绳子段。此时更好的方法是把绳子剪成长度为2的两段,因为2*2 > 3*1。

猜你喜欢

转载自blog.csdn.net/zqw_yaomin/article/details/81545679