劍指offer之剪繩子問題
1 問題
給你一根長度為n的繩子,請把繩子剪成m段 (m和n都是整數(shù),n>1并且m>1)每段繩子的長度記為k[0],k[1],…,k[m].
請問k[0] * k[1] …k[m]可能的最大乘積是多少?
例如,當繩子的長度為8時,我們把它剪成長度分別為2,3,3的三段,此時得到的最大乘積是18.
2 分析
1) 分析邊界值
n和m題目說了,要求大于1,所以繩子長度最少為2,而且每段的長度不能小于1。
當n=2的時候,我們只有一種分割方法,就是中間切斷,變成了1*1 = 1,所以當繩子長度為2的時候,切斷數(shù)長度乘積最大值為1
當n=3的時候,我們有2種分割方法,就是1*1*1 = 1,然后就是1*2 = 2,所以當繩子長度為2的時候,切斷數(shù)長度乘積最大值為2
當n=4的時候,我們有3種分割方法,就是1*1*1*1 = 1,然后就是1*2 *1 = 2,還有2 * 2 = 4,所以當繩子長度為2的時候,切斷數(shù)長度乘積最大值為4;
當n=5的時候,我們有4種分割方法,就是1*1*1*1*1 = 1,然后就是1* 2 *1 * 1 = 2,還有2 * 2 * 1 = 4,還有1 * 3 * 1 = 3,還有2 * 3 = 6;所以當繩子長度為2的時候,切斷數(shù)長度乘積最大值為6;
我們記最大值為f(n)為長度為n繩子剪成若干段的最大乘積,我們可以知道f(n) = max{f(i) * f(n - i)}, i ∈ {1,2...,n - 1},
f(n)的最優(yōu)解對應著f(i)和f(n-i)的最優(yōu)解,假如f(i)不是最優(yōu)解,那么其最優(yōu)解和f(n-i)乘積肯定大于f(n)的最優(yōu)解,然后f(i)又可以看成f(n),大問題包含小問題,并且大問題的最優(yōu)解包含著小問題的最優(yōu)解,所以可以使用動態(tài)規(guī)劃求解問題.
f(4) = max{f(1) * f(3), f(2) * f(2)} = 4;
f(5)= max{f(1) * f(4), f(2) * f(3)} = 6;
...
f(n) = max{f(1) * f(n -1), ... , f(i) * f(n - i), ... }; 由于具有對稱性,所以這里的i的范圍是 i ∈ {1, 2..., n / 2};
3 代碼實現(xiàn)
#include <stdio.h>
#include <stdlib.h>
int maxCutting(int len)
{
if (len <= 1)
{
return 0;
}
if (len == 2)
{
return 1;
}
if (len == 3)
{
return 2;
}
int *data = (int *)malloc(sizeof(int) * len + 1);
if (data == NULL)
{
printf("malloc data fail\n");
return 0;
}
data[0] = 0;
data[1] = 1;
data[2] = 2;
data[3] = 3;
int max;
for (int i = 4; i <= len; ++i)
{
max = 0;
for (int j = 1; j <= i / 2; ++j)
{
max = data[j] * data[i - j];
if (data[i] < max)
{
data[i] = max;
}
}
//printf("data[%d] is %d\n", i, data[i]);
}
return data[len];
free(data);
return 1;
}
int main(void)
{
int result = maxCutting(8);
if (!result)
{
printf("輸入繩子長度不合法或者開辟數(shù)組內(nèi)存失敗\n");
return -1;
}
printf("繩子長度為8的最大切割乘積值為%d", result);
return 0;
}
4 運行結(jié)果
繩子長度為8的最大切割乘積值為18
5 總結(jié)
大問題包含小問題,并且大問題的最優(yōu)解包含著小問題的最優(yōu)解,所以可以使用動態(tài)規(guī)劃求解問題.動態(tài)規(guī)劃我們可以理解成高中數(shù)學知識的數(shù)列關系,既迭代,我們最關鍵的是找到迭代關系,如上題f(n) = max(f(i) * f(n -1));
作者:chen.yu
深信服三年半工作經(jīng)驗,目前就職游戲廠商,希望能和大家交流和學習,
微信公眾號:編程入門到禿頭 或掃描下面二維碼
零基礎入門進階人工智能(鏈接)