一些问题从整体上看貌似无从下手,从上到下考虑各种细节会越来越复杂。这时候就可以考虑用动态规划算法解决了,一个小的局部问题解决了后,在找到状态转移方程,通过状态转移方程解决掉所有局部问题,遍历完成后,全局问题就解决了。
关键是要找到状态转移方程。
下面先举一个例子:找到数组中sum(和)最大的子数组(maximum sum subarray)。这是个经典的问题,详细可以百度之。
用tmax表示当前局部求得的最大sum,为求得下一状态tmax值,需要tmax+A[i]和当前数组元素A[i]比较:,tmax=max(tmax+A[i],A[i])。这就是状态转移方程。所以可以写出下面程序:
// this is DPsolution
int maxSum(int A[],int n)
{
assert(n>0);
if(n<=0) return 0;
if(n==1) return A[0];
int tmax= 0;
int result=A[0];
for(int i=0;i<n;i++)
{
tmax=max(tmax+A[i],A[i]);
result=max(tmax,result);
}
return result;
}
再举一例:找到数组中product(积)最大的子数组(maximum product subarray)。同上个问题类似,只不过是sum变作product。
同样用tmax表示当前局部求得的最大product。这是求状态转移方程稍微比上一例子复杂一点,考虑到数组中两个负数相乘会得到一个很大的正数。所以,还需要一个tmin来记录当前求得最小product。
下一状态的tmax值的更新,不仅需要比较tmax*A[i]和A[i],还需要比较tmin*A[i]。即tmax=max(max(tmax*A[i],A[i]),tmin*A[i])。而tmin=min(min(tmax*A[i],A[i]),tmin*A[i])。
程序如下:
// this is DP solution!!!haha
int maxProduct(int A[],int n)
{
assert(n>0);
if(n<=0) return 0;
if(n==1) return A[0];
int tmax=A[0];
int tmin=A[0];
int result=A[0];
for(int i=1;i<n;i++)
{
int tmax_copy=tmax;
tmax=max(max(tmax*A[i],A[i]),tmin*A[i]);
tmin=min(min(tmax_copy*A[i],A[i]),tmin*A[i]);
result=max(result,tmax);
}
return result;
}