区间dp的初次体会,超级基础超级详细

整体的思想

是对线性区间的最优问题的处理,每一个当前的区间的状态都可以由之前的某个区间的状态来处理即可。
写状态转移方程的时候,有人是把最开头的i当做和之前区间相比多出来的部分,有人把j当做和之前区间相比多出来的部分,两者都行还是要看个人喜好。

dp[i][j] = max(dp[i][j],dp[i][k] + dp[k+1][j] + ~~~)

初始化问题

流传比较广的是两种初始化方式,这里以求区间的最小值为例,两者并没有什么的大的区别,当然如果你是一个模板化的玩家,我还是比较建议使用第一种,从崔神的背包九讲学过来的就会知道,初始化是讲究一个合法与非法的问题,对于非法的子问题,其值一定是inf,对于合法的那么就是0,而第二种并没有那种严格的状态一说而已

  1. 整体初始化
memset(dp,inf,sizeof(inf));
for(int i = 1;i <= n;i++) dp[i][i] = 0;
  1. 逐个初始化
memset(dp,0,sizeof(dp));//相当于没有初始化
for(int len = 1;len <= n;len++){
	for(int i = 1;i + len - 1 <= n;i++){
		dp[i][j] = inf;
		for(int k = i;k < j;k++){
			//枚举断点
			dp[i][j] = min();
		}
	}
}

之后的所有初始化问题均由读者自己解决,不再赘述
好像有点模仿崔神(逃)

断点的枚举问题

对于n个数,他们之间的间隔有n-1个,所以k并不能取到[i,j]的所有
对于一般的区间问题常见的就有两种

  1. for(int k = i;k < j;k++)
  2. for(int k = i + 1;k <= j;k++)

状态转移方程的书写

dp的核心就是状态转移方程的书写,但是很多人都不会写,即使在见过很多题目之后也还是不会写,没思路,首先你要有一个明确的概念,

当前的状态都是由 最近的 之前的 状态生成的

什么意思呢,以一道题为例:

POJ Brackets (括号匹配)

题意:给定字符串找到所有子串的括号匹配的数目最大值
思路:首先要确定是一个线性的最优解的问题,其次就要想怎么做,首先枚举起点,枚举长度,找到区间,那么dp[i][j]如何才能永远代表着当前区间的最大值呢,假设新加入的是起始节点 i,那么当s[i]与s[j]匹配的时候,
dp[i][j] = dp[i+1][j-1] + 2;
不匹配就不需要更新,下一步是判断怎么个断点取最优,第一步要明确

取断点的目的在于什么

在于将区间分割,得到最优的两个子区间
比如:当前区间为 ( ) ( ( ) ) 那么很明显,我们分为哪两个子区间的时候最大呢,肯定是从右往左第二个括号的左边那里分,但是计算机并不知道啊,他需要暴力求解啊,那么怎么个求解法则呢,就是枚举断点不断分割成左右两个区间,取最大值即可。

是不是有点那意思了,接下来再看一道例题,再体会一下思想的升华

HDU Palindrome subsequence(回文子串数目)

今天先到这里了,还得训练,溜了溜了,各位看官有问题的话留言即可,我是活人(非机器人),

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值