动态规划具有两个属性,如果一个问题具有这两个属性,说明这个问题可以用动态规划来做。
(1)子问题重叠
(2)最优子结构
本文将用来讨论第一个属性。
动态规划主要用于需要反复解决相同子问题的情况。在动态规划中,子问题的计算解决方案存储在表中,因此不必重新计算这些问题。因此,在没有常见(重叠)子问题时,动态编程就没有用。
案例一:Fibonacci Numbers
方法一:递归
public int fib(int n)
{
if(n<=1)
return n;
return fib(n-1)+fib(n-2);
}
缺点:我们根据画出递归树可以很明显看出,有许多重复计算的分支。于是我们应用动态规划,。我们使用表来将我们已经计算过的存储起来。
动态规划中用表存储子问题一般有两种方法:
- 记忆法,自上而下,按需填表
- 制表法,自下而上,从下面的条目开始,一个一个向上填充。
方法二:记忆法存储重叠子结构
public int fib2(int n)
{
//声明一个数组用于存放已经计算过的子问题
if(lookup[n]==-1)
{
//如果这个子问题没有计算过,则和递归中处理相似,只是值不是直接返回,而是存入数组中,
if(n<=1)
lookup[n]=n;
lookup[n]=fib2(n-1)+fib2(n-2);
}
//如果这个数组计算过,则直接返回计算过的值
return lookup[n];
}
方法三:利用制表法存储已经解决的子问题
{
//声明一个表格长度为n+1的数组,用于存储
int[] table=new int[n+1];
//由下到上,前两个条目先明确
table[0]=0;
table[1]=1;
//通过循环迭代,计算出传入的值 。
for(int i=2;i<=n;i++)
{
table[i]=table[i-1]+table[i-2];
}
return table[n];
}
案例二:Ugly Numbers
Ugly Numbers 是只包含质因子2、3和5的数。
怎么获得ugly numbers?
首先除2,直到不能整除为止,然后除5到不能整除为止,然后除3直到不能整除为止。最终判断剩余的数字是否为1,如果是1则为丑数,否则不是丑数。
题目:寻找出第n个丑数。
方法一:逐一遍历
public int maxdivide(int a,int b)
{
//a 除以b的最大可乘幂
while(a%b==0)
a=a/b;
return a;
}
public boolean isUgly(int num)
{
//判断一个数是不是丑数
num=maxdivide(num,2);
num=maxdivide(num,3);
num=maxdivide(num,5);
return num==1;
}
public int uglyNumbers(int n)
{
//在第n个丑数前面的整数每个都进行是否是丑数的判断。
int count=1;
int i=1;
while(count<n)
{
i++;
if(isUgly(i))
count++;
}
return i;
}
方法二:动态规划法
具有重叠子问题,我们把重叠的子问题的结果存储下来,利用制表法,由下至上。
因为每个丑数都能被2,3,5除,所以我们可以把丑数表示为下列三种群体:
(1) 1×2, 2×2, 3×2, 4×2, 5×2, …
(2) 1×3, 2×3, 3×3, 4×3, 5×3, …
(3) 1×5, 2×5, 3×5, 4×5, 5×5, …
将上述群体存放到数组中,利用三个指针i2,i3,i5:
第一个是1,第二个通过在三个群体中找到最小值,并将其存入,然后将其指针下移,重复上述步骤。
public int UglyNumbers(int n)
{
//声明一个数组用于存放丑数
int[] ugly_num=new int[n];
int i2=0,i3=0,i5=0;
//自下而上,第一个是1,
ugly_num[0]=1;
int next_mulitple_of_2=2;
int next_mulitple_of_3=3;
int next_mulitple_of_5=5;
for(int i=1;i<n;i++)
{
ugly_num[i]=Math.min(next_mulitple_of_2, Math.min(next_mulitple_of_3, next_mulitple_of_5));
if(ugly_num[i]==next_mulitple_of_2)
{
i2++;
next_mulitple_of_2=2*ugly_num[i2];
}
if(ugly_num[i]==next_mulitple_of_3)
{
i3++;
next_mulitple_of_3=3*ugly_num[i3];
}
if(ugly_num[i]==next_mulitple_of_5)
{
i5++;
next_mulitple_of_5=5*ugly_num[i5];
}
}
return ugly_num[n-1];
}