先看三则算法的代码:
0-1 背包问题:
int dpf[N+1][W+1]; //数组从0开始
int dp_solve()
{
for(int i=0; i<N;i++)
for(int j=0; j<=W;j++)
if(j<w[i])
dpf[i+1][j]=dpf[i][j];
else
dpf[i+1][j]=max(dpf[i][j],dpf[i][j-w[i]]+v[i]);
return dpf[N][W];
}
完全背包问题:
int dp[N+1][W+1]; //数组从0开始
int dpsolve()
{
for(int i=0; i<N;i++)
for(int j=0; j<=W;j++)
if(j<w[i])
dp[i+1][j]=dp[i][j];
else
dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]);
return dpf[N][W];
}
LCS最大公共子序列问题:
int dp[m+1][n+1];
int Lcs()
{
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
if(s1[i]==s2[j])
dp[i+1][j+1]= dp[i][j]+1;
else
dp[i+1][j+1]= max(dp[i+1][j],dp[i][j+1]);
return dp[m][n];
}
0-1 背包问题 (一维数组DP):
int dp[W+1]; //数组从0开始
int dp_solve()
{
for(int i=0; i<N;i++)
for(int j=W; j>=w[i];j--)
dp[j] = max( dp[j], dp[j-w[i]]+v[i]);
return dp[W];
}
完全背包问题(一维数组DP):
int dp[W+1]; //数组从0开始
int dp_solve()
{
for(int i=0; i<N;i++)
for(int j=w[i]; j<=W;j++)
dp[j] = max( dp[j], dp[j-w[i]]+v[i]);
return dp[W];
}
而对于LCS,我们无能为力,分析其原因,可以发现,当 DP算法当前的状态 dp[i+1][j+1] 和 dp[i][j] , dp[i-p][j-q] 等值相关时,如下图所示D 为当前状态,C代表同行前置状态,B代表同列上层状态, A 代表D的上层并且前置状态 。
设 D = max { fun(A), fun(B), fun(C) }
(1) 当 D = max { fun(A), fun(B) } 即D仅和 A 、B 相关,可以使用一维数组优化,其二级循环 为 从大到小,(因为要使用到 上一层A 状态相关,从小到大 会更新A 的状态),如下代码:
for(int i=0; i<N;i++)
for(int j=M; j>=0;j--)
// loop codes
(2) 当 D = max { fun(B), fun(C) } 即D 仅和 B、C相关,可以使用一维数组优化,其二级循环为从小到大,(因为要使用本层状态C的最新值,所以需要先更新C,再考虑D),如下代码:
for(int i=0; i<N;i++)
for(int j=0; j<=M;j--)
// loop codes
(3) D = max { fun(A), fun(C) } 即 D 和 A、C 存在关联时,不可以使用一维数组优化,因为 D 和 A 相关,那么要求 二级循环从大到小,而D和C相关,却要求二级循环从小到大。矛盾,所以不可行。
当我们遇到一维数组无法优化 二级 DP 内存消耗时,我们也可以用一个绝对靠谱的方法,也就是交替法,使用两个一维数组搞定(如 dp[2][M]).
具体方法很简单,比如 0=1 背包问题的N*W二维数组解法为:
int dpf[N+1][W+1]; //数组从0开始
int dp_solve()
{
for(int i=0; i<N;i++)
for(int j=0; j<=W;j++)
if(j<w[i])
dpf[i+1][j]=dpf[i][j];
else
dpf[i+1][j]=max(dpf[i][j],dpf[i][j-w[i]]+v[i]);
return dpf[N][W];
}
那么使用2×W(等价于两个一维数组的方法)只需要 把 dpf数组的行下标变量 i 和 i+1 改成 i&1 和 (i+1)&1, 该方法使用的性质是,【偶数 &1 = 0】 , 【奇数&1=1】,如下所示:
int dpf[2][W+1]; //数组从0开始
int dp_solve()
{
for(int i=0; i<N;i++)
for(int j=0; j<=W;j++)
if(j<w[i])
dpf[(i+1)&1][j]=dpf[i&1][j];
else
dpf[(i+1)&1][j]=max(dpf[i&1][j],dpf[i&1][j-w[i]]+v[i]);
return dpf[N][W];
}