最常考察的动态规划算法(共七个)

本文详细解析了五种经典动态规划问题:最大连续子序列和、最长不下降子序列、最长公共子序列、最长回文子串及DAG最长路径,并提供程序模板与注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文一方面用于自身备考,一方面进行整理和总结注意点。
参考《算法笔记》
基本结构是:

  • 类型介绍
  • 程序模板
  • 注意点

一、最大连续子序列和

1.类型介绍

给定一个数字序列A1,A2……An,求i,j使得Ai+……+Aj最大,输出最大和。
样例:-2 11 -4 13 -5 -2
最大和为11+(-4)+13=20

2.模板
//第一步,令dp[i]表示以A[i]为末尾的连续序列最大和
//第二步,状态转移方程
dp[0]=A[0];
for()
{
	dp[i]=max(A[i],dp[i-1]+A[i]);
	//只有加Ai大于Ai本身,才有往前取得必要
}
for()
{
//选出最大的和
}
3.注意点
  1. 动态规划要注意的问题主要就是状态转移方程,一旦确定了方程,则一切迎刃而解
  2. 因为这里对于每个dp只有两种情况:加上Ai大于其本身或者其本身。
  3. 对于动态规划要注意一般情况下前几个dp应该要进行赋值确定。

二、最长不下降子序列

1.类型介绍

一个数字序列中,找到一个最长得子序列(可以不连续),使得这个子序列是不下降(非递减)的。
样例:{1,2,3,-1,-2,7,9}
最长子序列:1,2,3,7,9,长度为5

2.模板
int ans=-1;//记录最大的dp
for(i)//遍历每一个dp
{
	dp[i]=1;//边界初始条件(即先假设每个元素自成一个子序列)
	for(j)//从头到i遍历,i之前的
	{
	//如果当前值小于之前值,并且之前值对应的dp+1大于当前值对应的dp,改变
		if(A[i]>=A[j]&&(dp[j]+1>dp[i]))
		{
			dp[i]=dp[j]+1;//状态转移方程
		}
	}
	ans=max(ans,dp[i]);//更新最大的dp
}

3.注意点
  1. 该程序实质上进行了二重遍历,外层是依次遍历Ai,内层则对Ai之前的j个值进行遍历。如果满足了条件,即将Ai接在后面能够增大长度。
  2. 注意根据题目逻辑分析,而不要盲目对dp的方程使用max
  3. 这里每个dp初始值设为1,设置边界,符合逻辑也方便比对

三、最长公共子序列

1.类型介绍

给定两个字符串A和B,求一个字符串,使得这个字符串是A和B的最长公共部分
样例:A=“sadstory” B=“adminsorry”
最长公共子序列:“adsory”

2.模板
int dp[N][N];//dp[i][j]表示A的i号位和B的j号位之前的最长公共子序列
//!!!注意,AB的读入下标要从1开始
int lenA=strlen(A+1);
int lenB=strlen(B+1);
//边界设置
//因为字符串从前向后匹配,即dp从左上角向右下角走,所以将左侧和上侧边界设0
for()
{
	dp[i][0]=0;
}
for()
{
	dp[0][j]=0;
}
//状态转移方程
for(i)//遍历A
{
	for(j)遍历B
	{
		if(A[i]==B[j])//如果两值相等,则加一
		{
			dp[i][j]=dp[i-1][j-1]+1;
		}else{//若不等,则取左侧或者上侧
			dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
		}
	}
}
//答案为dp[lenA][lenB]

3.注意点
  1. 这道题明显的变化是,dp变成了二维数组,这是基于题目所给两个字符串考虑的。
  2. 这道题AB两个数组值下标从1开始,0是边界

四、最长回文子串

1、类型介绍

给出一个字符串S,求S的最长回文子串长度
样例:“PATZJUJZTACCBCC”
最长:“ATZJUJZTA”,长度9

2.模板
int dp[maxn][maxn];
memset(dp,0,sizeof(dp));
//边界初始化
for(i)
{
	dp[i][i]=1;
	if(S[i]==S[i+1])
	{
		dp[i][i+1]=1;//判断紧密相接的元素
		ans=2;//初始化时注意当前最长回文子串长度
	}
}
//状态转移方程
//枚举的是长度,从3开始
for(L=3;L<=len;L++)//枚举子串长度
{
	for(int i=0;i+L-1<len;i++)
	{
		int j=i+L-1;
		if(S[i]==S[j]&&dp[i+1][j-1]==1)
		{
			dp[i][j]=1;
			ans=L;
		}
	}

}
3.注意点
  1. 这里不是从前往后从小到大的遍历,而是枚举长度从小到大进行遍历。
  2. 初始化dp[i][i]=1即本身一定与本身回文。
  3. 因为是从长度3开始遍历,则初始化时要考虑L=2的可能性

五、DAG最长路

一、类型介绍

给出一个有向无环图DAG,解决两个问题:

  1. 求整个DAG中最长路径(不固定起点和终点)
  2. 固定终点,求DAG的最长路径
题型1

解决办法是令dp[i]表示从i号顶点出发能够获得的最长路径长度,这样所有的dp[i]的最大值就是整个DAG的最长路径长度。

二、模板
int DP(int i){
	if(dp[i]>0)return dp[i];//dp[i]已计算得到,直接返回
	for(int j=0;j<n;j++)	//遍历i的所有出边
	{
		if(G[i][j]!=INF)//如果存在出边
		{
			int temp = DP(j)+G[i][j];	//计算出边对应点加上ij之间的距离
			if(temp>dp[i])			//如果路径更长
			{
				dp[i]=temp;				//覆盖
				choice[i]=j;			//i的后继顶点是j
			}
		}
	}
	return dp[i];					//返回计算后的dp[i]
}
//需要得到最大的dp[i],然后将i作为路径起点传入
void printPath(int i)
{
	printf("%d",i);
	while(choice[i]!=-1)//choice数组初始化为-1
	{
		i=choice[i];//得到后继节点
		printf("->%d",i);
	}


}
三、注意点

六、背包问题

两种最简单常见的背包问题:

  1. 01背包问题
  2. 完全背包问题
1.01背包问题

01背包问题是多阶段动态规划问题,即每个阶段的状态只和上一个阶段的状态有关。

1.类型介绍

有n件物品,每件物品的重量为w[i],价值为c[i]。现有一个容量为V的背包,问如何选择物品放入背包,使得背包内物品的总价值最大。其中每种物品都只有一个。

2.模板
for(int i=1;i<=n;i++)
{
	for(int v=w[i];v<=V;v++)
	{
		dp[i][v]=max(dp[i-1][v],dp[i-1][v-w[i]]+c[i]);
	}
}
2.完全背包问题
1.n件物品,每种物品的单件重量为w[i],价值为c[i]。现有一个容量为V的背包,问如何选择物品放入背包,使得背包内物品的总价值最大。其中每种物品都有无穷件。
2.模板:
for(int i=1;i<=n;i++)
{
	for(int v=w[i];v<=V;v++)
	{
		dp[v]=max(dp[v],dp[v-w[i]]+c[i]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值