基础DP总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础DP总结相关的知识,希望对你有一定的参考价值。
---恢复内容开始---
基础DP总结
关键词:基础DP问题,LIS,LCS,状压DP
简析 :DP大法好啊,当一个大问题不好解决的时候,我们研究它与其子问题的联系,然后子问题又找它的子问题,如此下去,一直推,一直减小到可以轻而易举求出答案(称为边界)。所以解决DP问题就是要推出一个正确的递推式。
一、DP解决基础递推问题
1)斐波那契数列
dp[n] = dp[n-1] + dp[n-2]
边界:dp[0] = 0 , dp[1] = 1 .
2 )Max sum
题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=1003
dp[n]表示从1~n段的最大子串和
用sum存储第n个数字能和前面的数字构成的最大和
可得 : dp[n] = max( dp[n-1] , sum )
边界:求出dp[1]
只有一个状态,一个for循环即可,O(N).
3)经典DP问题:数塔
题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=2084
分析 : 数塔就像一个满二叉树啊。我到底是走左边还是走右边能获得最大值呢?然后每一个点都能这样考虑,就是一个递归的问题了,普通的递归必然 时间复杂度过大,那再记忆化递归,求完每点的答案用一个数组存储,那就可以了。其实这已经是DP的解法了,下面来看看标准的DP解法
dp[i][j] : 储存从高度为i,左边数第j个节点走到底的数字和最大值
dp[i][j] = max( dp[i-1][j] , dp[i-1][j+1] )
边界:求出底层 dp[0][j] 的值
代码参考 : 题目链接里的discuss
二、LIS,LCS问题
仅作简要分析
1)LIS(最长递增子序列)
分析 :从DP的视角来分析,假设子状态均已求出,怎么通过转移得到答案呢?单单从dp[n-1]转移得到的答案是正确的吗?
dp[n]=dp[n-1];
for(i = 0; i<n;i++)
{
if(a[n]>a[i])
dp[n]=max(dp[n],dp[i]+1);
}
时间复杂度:O(N*N)
然而这个复杂度在ACM中是过不了题的,对其优化就在于对子状态的选取,来取代遍历。二分的办法让复杂度降到了O(N*logN)。
int ans[MAX_N], a[MAX_N], dp[MAX_N], n; // ans 用来保存每个 dp 值对应的最小值,a 是原数组
int len; // LIS 最大值
ans[1] = a[1];
len = 1;
for (int i = 2; i <= n; ++i) {
if (a[i] > ans[len]) { //改成>=的话,下面
ans[++len] = a[i];
} else {
int pos = lower_bound(ans, ans + len, a[i]) - ans; //改成upper_bound
ans[pos] = a[i];
}
}
cout << len << endl; // len 就是最终结果
2)LCS(最长公共子序列)
dp[i][j]: A串0 ~ i 与B串0 ~ j 的最长公共子序列
DP分析:还是当dp[i][j]之前的状态已知,怎么推出dp[i][j]。
I :dp[i][j] = max( dp[i-1][j] , dp[i][j-1] );
II : 如果字符 i 刚好和字符 j 匹配,则dp[i][j] = dp[i-1][j-1] + 1
想不到还有其它的可能了。
题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=1159
题解:
#include<stdio.h>
#include<string.h>
char s1[1005], s2[1005];
int dp[1005][1005];
int max(int x,int y)
{
if(x>y) return x;
else return y;
}
int main(){
int len_s1,len_s2,i,j;
while(~scanf("%s%s", s1, s2)){
len_s1 = strlen(s1), len_s2 = strlen(s2);
memset(dp, 0, sizeof(dp));
for( i = 0; i < len_s1; i++)
for( j = 0; j < len_s2; 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]);
printf("%d\n",dp[len_s1][len_s2]);
}
}
注意:边界处理(i或j为0,1的时候)
三、状压DP(二进制枚举状态)
稍后补。。。
---恢复内容结束---
以上是关于基础DP总结的主要内容,如果未能解决你的问题,请参考以下文章