动态规划

本文深入探讨了动态规划的概念、特点和常见应用场景。通过介绍动态规划的步骤、区分点以及经典问题,如最长上升子序列、最大子段和问题、数字金字塔等,帮助读者理解动态规划的决策过程和状态转移方程。此外,文章还提供了多种动态规划问题的解决方案,包括01背包问题、完全背包问题、分组背包问题等,旨在帮助读者掌握动态规划的实用技巧。
摘要由CSDN通过智能技术生成

动态规划

特点:

1.要将事物合并,“汇聚”
2.要通过前面的值得到当前的值
3.最优解结构——只考虑上面部分(如在图中,就只走右和下)

绝招:

1.字符串 画表格法,因为dp常用二维数组,故可以尝试画表格(行列)来自己模拟出答案,再找前,上,对角线的规律,来得出方程
注意:
先不管0行0列,最后再想初始值,画表格一定要保证自己做,准确
2.最优化问题-》考虑解的结构
例1:最大字段和问题:解是由一个左端点和一个右端点构成,对每个有端点来求答案,当固定又右端点时,只需要求左边的信息,并且每个左端点都是有子结构的
例2:最长上升子序列:以每个为末位,则找其前驱,那么每个问题就变成以其为末位的子问题

步骤:

1.定状态:把大问题分为若干相似小问题(dp数组的意义)
注意如何设计状态是状态和状态间是包含关系
2.分阶段
3.做决策
4.写方程

区分:

dp:状态转移方程
线段树:动规+回溯,通过多子节点推出父亲节点
贪心:要最优解

问题:

计数类问题
数据范围几千,答案要对某数取余

简介

思想:循环过程中,保证i位置以前都为最优解。

原理:此问题通过局部的最优解,能导致整个问题的全局最优解。(废话)

原则:无后效性,即后面的数确定之后,不会影响(改变)前面的数。

常识
1.动态规划一般用dp[ ]或dp[ ][ ]来表示。
2.


forint i=0;i<n;i++{
   
	for(int j=0;j<i;j++)//第二个循环小于的值一定是i
	{
   
		if(xxx)
		dp[i]=max(dp[i],dp[j]+1);
	}
}

例子解释:
1.第二个循环小于的值一定是i,因为我们要找的是到第I个时的最优解。(从局部最优解到全局最优解),但这里要注意下除1—i之外也有可能是i—1.
2.max(dp[i],dp[j]+1) 这里有两种情况1i的值较大,j的值较小,

注意事项
1.动态规划一般通过一个或两个循环(不固定)进行求解。
2.如果要用两个循环,注意是否会超时,合理选择优化方案。
3.动态规划求解顺序自底向上.

状态总结:

区间:求出以每个点为右端点的答案
子序列:求出以每个点为结尾的答案
矩阵(矩形):求出以每个点为右下角

最长相同子串,序列

子串:保证要是连续的。
子序列:不用连续的。
法1:
两个子序列:X,Y
取两个结尾X[a],Y[b],若X[a]==Y[b],就在往前找,成为前面的问题的子问题
状态:f[i][j]表示,到X的第i项,Y的第j项,能得到的最长子序列(X序列的前i项,Y序列的前j项能构成的最长公共子序列的长度)
决策:
若X[i]==Y[j],就可以往前找,找X[1到i-1]和Y[1到j-1]的最大值
若不等,(1)放弃X[i],找X[i到i-1]和Y[1到j]的最大值 (2)放弃Y[j],同理
方程

if(i==0 || j==0) f[i][j]==0;
else if(x[i]==y[j])f[i][j]=f[i-1][j-1]+1;
else f[i][j]=max(f[i-1][j],f[i][j-1]);

法2:
思路:
最长相同子序列:
if 只要发现相同的字符。那么,dp[i][j]这个值就是从dp[i][j]和dp[i-1][j-1]中直接来选取的。
else 而另外一种字符不相同的情况。因为这是二维数组,所以就dp[i][j]有两个上一个分别为dp[i-1][j]和dp[i][j-1],于是就从这两个值中选取最大值。

最长相同子串
与最长相同子序列类似,只不过else(当不满足条件时)不取最大值了。而是要将dp[i][j]变为1

下面贴代码

for(int i=1;i<=n;i++)//n为第一个字符的长度
{
   
	for(int j=1;j<i;j++)//m为第二个字符的长度
	{
   
		if(dp[i][j]==dp[i][j-1])
		{
   
			dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1;
		}else{
   
			dp[i][j]=max(dp[i-1][j],dp[i][j-1];//这是相同子序列的
			dp[i][j]=1;//这是子串的
		}
	}
}

注意
最长子序列输出的是dp[n][m]
最长子串输出的是数组中的最大的值

最长上升子序列

要求:
1.子序列-》阶段:从左到右
2.单调-》条件
状态:f[i]表示以第i个数为结尾的最长上升子序列
决策:第i个数与其左边小于a[i]的数中,f值最大的一个组成
找到i的一个 前驱j(a[j]<a[i]),就变成了以j为结尾的最长上升子序列问题
f[i]=1+max(f[j])

数字金字塔

在这里插入图片描述
1.将金字塔转为一张图(但注意转换后,dp转移方程是下方和右下方)
2.a[x][y]指(x,y)上的值,f[x][y]为到(x,y)的最大值
方程:f[x][y]=a[x][y]+max(f[x+1][y],f[x+1][y+1])——要从下往上搜

最大子段和问题:

连续字段和:
连续子段->区间 [l,r+1]=[l,r]+G[r+1]
要求:1.子序列2.单调
普通:dp[i]=max(dp[i-1]+a[i],a[i]):
环形问题:问题转换
分类讨论
1.最大子段在中间:普通做法
2.跨越首位:算出中间段的最小字段和,在用总和减去这个值
注意:如果全是负数,找一个最大值即可
两段问题:
dp1[i]记录从i开始的最大子段,dp2[i]记录到i结束的最大子段,这样最后只用枚举分割点即可
环状两段问题:
细节:n不能<=3,注意正数个数<1

建别墅:

在这里插入图片描述
以每个点为右下角,看能形成的正方形边长是多少
在这里插入图片描述
故若有以(x,y)为右下角,边长为k的正方形,就必须满足:
1.(x,y)为1
2.有以(x,y-1)和(x-1,y)为右下角的边长为k-1的正方形
3.左端点为1

阶段:从上往下,从左往右
状态:以(i,j)为右下角的正方形的最大边长
方程:f[i][j]=min(f[i-1][j],f[i]][j-1])+1

开垦农田

在这里插入图片描述
矩形:二维区间
以(x1,y1)为左上,(x2,y2)为右下
当把一维确定后,就是求另一维的最大子段和问题
在这里插入图片描述
sum[3][6][k]=-8 -3 19 15 7 -2 0
f[3][6][k]= -8 -3 19 34 41 39 39(f的意义是以k行作为结尾)

for(int i=1;i<=n;i++){
   
	for(int j=1;j<=n;j++){
   
		scanf("%d",&t);
		sum[i][j]=sum[i-1][j]+t;//预处理,前缀和
	}
}
for(int i=1;i<=n;i++){
   
	for(int j=i;j<=n;j++){
   
		for(int k=1;k<=n;k++){
   
			t=sum[j][k]-sum[i-1][k];
			dp[i][j][k]=max(dp[i][j][k-1]+t,t);
			maxn=max(maxn,dp[i][j][k]);
		}
	}
}

区间动规:

特点:只要决策确定,不用管区间内部是如何取值。
dp[x][y]表示x到y的区间
1.由两个长度较小的区间推得dp[i][j]=max(dp[i][j],dp[i][j-1]+… ,dp[i+1][j]+…)
2.枚举断点dp[i][j]=max(dp[i][j],dp[i]][k]+dp[k+1][j]+sum(i,j))
求sum(i,j):
1.sum[i][j]=sum[i][j-1]+a[j]
2.
pre[i]=pre[i-1]+a[i];
sum[i][j]=pre[j]-pre[i-1]
问题:
n<3000,和字符串有关
策略:
1.枚举所要求的的区间长度Li
2.枚举起点i
3.根据Li得到区间终点j=i+L-1;
4.枚举i到j之间的所有断点k
L从2开始,因为长度至少为1,小于等于n
断点k是从i到j,故i<=k<=j-1
根据(i,k)+(k+1,j)转移到(i,j),因为是按区间长度进行dp,且(i,k)和(k+1,j)的长度都比(i,j)小,故已经算过

合并石子

关键:1.圆形 2.合并相邻的=>区间
做法:
1.先考虑合并的过程
从最后一步分析:最后一步只剩最后两个区间,即合并一个前缀和一个后缀
每次合并都是区间(相邻两堆),每个区间又是以两个区间合并出的
状态:f[i][j]表示从第i堆到第j堆合并出的最大值
方程:f[i][j]=max(f[i][k]+f[k+1][j]+sum(i,j) | i<=k<=j )
在这里插入图片描述
2.考虑圆形
拆环
原理:必存在一条分割线,使左右两边不合并
方法:复制

乘积最大问题:

加入乘号
注意:用字符串输入

const int N = 100;
const int K = 100;

int n, k;
char s[N + 10];
//dp[i][j]=max(dp[i-1][k]*s[k][j],dp[i][j])注意这里s一定从l+1到i,因为dp表
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值