挑战程序设计竞赛-DP

DP的实质:记忆化搜索.DP只是一种记忆化的枚举

对于一个完全决策问题,全部搜索是2^n的时间,但是这其中包含了很多重复

朴素的背包算法

int W[maxn];
int V[maxn];
int N;
int res(int curi,int curj){
	if(curi==N) return 0;//没有剩余物品
	int result;
	if(curj<W[curi]) result = res(curi+1,curj)
	else result=max(res(curi+1,curj),res(curi+1,curj-W[curi])+V[curi]);
	return result;
}

这是2^N的解法,遍历了所有可能的背包

注意理解curi==N的做法,没有物品的时候,是不能增加贡献的。

在这里由于都是相加然后return,单独拿出每一步来看,这个函数res(curi+1)计算的都是以后的,与现在的情况无关。


如果使用记忆化搜索,每次记录当前物品和重量的情况

</pre><pre name="code" class="cpp">int dp[maxn][maxn];
memset(dp,0,sizeof(dp));
int memory(int curi,int curj){
	if(!dp[curi][curj]) return dp[curi][curj];
	int res;
	if(curi==N) res=0;
	else if(curj<W[curi]) res=memory(curi+1,curj);
	else res=max(memory(curi+1,curj),memory(curi+1,curj-W[curi])+V[curi]);
	return dp[curi][curj]=res;
}

如果采用记忆化方式,由于i,j只有N*W种可能,所以O(N*W)




DP实质的理解



当你企图使用计算机解决一个问题是,其实就是在思考如何将这个问题表达成状态(用哪些变量存储哪些数据)以及如何在状态中转移(怎样根据一些变量计算出另一些变量)。所以所谓的空间复杂度就是为了支持你的计算所必需存储的状态最多有多少,所谓时间复杂度就是从初始状态到达最终状态中间需要多少步!


如果一个阶段的最优无法用前一个阶段的最优得到呢?

什么你说只需要之前两个阶段就可以得到当前最优?那跟只用之前一个阶段并没有本质区别。最麻烦的情况在于你需要之前所有的情况才行。

再来一个迷宫的例子。在计算从起点到终点的最短路线时,你不能只保存当前阶段的状态,因为题目要求你最短,所以你必须知道之前走过的所有位置。因为即便你当前再的位置不变,之前的路线不同会影响你的之后走的路线。这时你需要保存的是之前每个阶段所经历的那个状态,根据这些信息才能计算出下一个状态!

每个阶段的状态或许不多,但是每个状态都可以转移到下一阶段的多个状态,所以解的复杂度就是指数的,因此时间复杂度也是指数的。哦哦,刚刚提到的之前的路线会影响到下一步的选择,这个令人不开心的情况就叫做有后效性。


契机就在于后效性。

有一类问题,看似需要之前所有的状态,其实不用。不妨也是拿最长上升子序列的例子来说明为什么他不必需要暴力搜索,进而引出动态规划的思路。

假装我们年幼无知想用搜索去寻找最长上升子序列。怎么搜索呢?需要从头到尾依次枚举是否选择当前的数字,每选定一个数字就要去看看是不是满足“上升”的性质,这里第i个阶段就是去思考是否要选择第i个数,第i个阶段有两个状态,分别是选和不选。哈哈,依稀出现了刚刚迷宫找路的影子!咦慢着,每次当我决定要选择当前数字的时候,只需要和之前选定的一个数字比较就行了!这是和之前迷宫问题的本质不同!这就可以纵容我们不需要记录之前所有的状态啊!既然我们的选择已经不受之前状态的组合的影响了,那时间复杂度自然也不是指数的了啊!虽然我们不在乎某序列之前都是什么元素,但我们还是需要这个序列的长度的。所以我们只需要记录以某个元素结尾的LIS长度就好!因此第i个阶段的最优解只是由前i-1个阶段的最优解得到的,然后就得到了DP方程
LIS(i)=max\{LIS(j)+1\} \ \ \ \ j<i \ and\ a[j] < a[i]

所以一个问题是该用递推、贪心、搜索还是动态规划,完全是由这个问题本身阶段间状态的转移方式决定的!

每个阶段只有一个状态->递推;
每个阶段的最优状态都是由上一个阶段的最优状态得到的->贪心;
每个阶段的最优状态是由之前所有阶段的状态的组合得到的->搜索;
每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->动态规划。

每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到
这个性质叫做最优子结构;

而不管之前这个状态是如何得到的
这个性质叫做无后效性。

另:其实动态规划中的最优状态的说法容易产生误导,以为只需要计算最优状态就好,LIS问题确实如此,转移时只用到了每个阶段“选”的状态。但实际上有的问题往往需要对每个阶段的所有状态都算出一个最优值,然后根据这些最优值再来找最优状态。比如背包问题就需要对前i个包(阶段)容量为j时(状态)计算出最大价值。然后在最后一个阶段中的所有状态种找到最优值。

作者:王勐
链接:https://www.zhihu.com/question/23995189/answer/35429905
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。




你可能感兴趣的:(挑战程序设计竞赛-DP)