动态规划与贪心算法_剪绳子问题

问题:

给你一根长度为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。

求解:

1.动态规划
求一个问题的最优解(最大值或最小值),而且该问题能够分解成若干子问题,并且子问题之间还有重叠的更小的子问题,可以考虑动态规划
(1) 分析是否能把大问题分解成小问题
(2) 分解后的每个小问题存在最优解
(3) 小问题的最优解组合起来能得到整个问题的最解

2.贪婪算法求解:
每一步都可以做出一个贪婪的选择,基于这个选择,确定能够得到最优解
1. 贪心算法在对问题求解时,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解
2. 选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关
3. 题目贪心策略:当n>=5时,尽可能多地剪长度为3的绳子;当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子

 

1.首先用动态规划递归实现,动态规划实际上就是用空间效率换区时间效率的一种做法:

public class Test {
public static int getMax(int n){
     //要求长度n>1,所以这里返回0表示输入非法

if(n<2){
return 0;
}
     //长度为2时,要求剪下段数m>1,所以最大是1x1=1

if(n==2){
  return 1;
}
     //长度为3时,要求剪下段数m>1,所以最大是1x2=2

if(n==3) {
return 2;
}
//存储长度从 0-n 的最大结果
int[] result=new int[n+1];
result[0]=0;
result[1]=1;
result[2]=2;
result[3]=3;
//自底向上开始求解
int max=0;
for(int i=4;i<=n;i++){
       //每次将最大乘积清空

max=0;
       //因为要计算f(j)乘以f(i-j)的最大值,j超过i的一半时是重复计算,所以只需要考虑j不超过i的一半的情况

for(int j=1;j<=i/2;j++){
         //计算f(j)乘以f(i-j)

int tempResult=result[j]*result[i-j];
if(max<tempResult) {
max = tempResult;
}
         //更新表中的f(i)=max(f(j)·f(i-j))

result[i]=max;
}
}
max=result[n];
return max;
}

}

2.采用贪婪算法得到最优解:
public static int getMaxValue(int n){
if(n<2) {
return 0;
}
if(n==2) {
return 1;
}
if(n==3) {
return 2;
}
//先求出长度为3的段一共有多少段
int countOfThree=n/3;
//再看除了长度为3的段,还剩下多少米,如果还剩1米则表示有(n-1)个3和一个4,
// 所以要把上一步算出来的3的个数减1,把最后的4剪成两段长度为2的绳子
if(n-countOfThree*3==1){
countOfThree-=1;
}
//当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子
int countOfTwo=(n-countOfThree*3)/2;
//计算所有的3的countOfThree次方,再乘以剩下2的countOfTwo次方
return (int) ((Math.pow(3, countOfThree))*(Math.pow(2, countOfTwo)));
}
 

猜你喜欢

转载自www.cnblogs.com/cdlyy/p/12153369.html