【白话算法】动态规划算法什么时候能用一维数组解问题

先看三则算法的代码:

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背包问题 和 完全背包问题可以简化为使用一个 数组来实现,如下:

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];
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值