DP算法-背包问题与线性DP问题(Acwing)

目录

一、何为DP

二、背包问题

        1、01背包问题

        2、完全背包问题

        3、多重背包问题

三、线性DP问题(典型例题与总结)

                1、数字三角形问题

                        2、最长上升子序列

                        3、最长上升子序列Ⅱ

                        4、最长公共子序列

                        5、最长公共子序列Ⅱ

                        6、最短编辑距离

         四、解题总结

一、DP问题

DP即为动态规划问题,

可以使用动态规划的问题一般都有一些特点可以遵循。如题目的问法一般是三种方式:

1.求最大值/最小值

2.求可不可行

3.求方案总数

如果碰到一个问题,是问这三个问题之一的,就有90%概率是使用动态规划来求解。

重点说明的是,如果一个问题是让求出所有的方案和结果,则肯定不是使用动态规划。

DP分析过程:

DP算法-背包问题与线性DP问题(Acwing)_第1张图片


二、背包问题

       f[i][j]表示从前i个物品中选且体积不超过j的最大价值

        以最后一个物品选多少个进行分类

        1、01背包

有 N件物品和一个容量是 V的背包。每件物品只能使用一次

第 i件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

for(int i=1;i<=n;i++){
		for(int j=1;j<=vv;j++){
			if(j>=w[i]){
				f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
				
			}
			else{
				f[i][j]=f[i-1][j];
			}
		}
		
	}

DP算法-背包问题与线性DP问题(Acwing)_第2张图片

只考虑最后一个物品选不选。

若不选,那就从1~i-1中选,且总体积不超过j,表示f[i-1][j]。

若不理解表达式,将其带入至初始的f[i][j]中文表示。例如:从前i-1个物品中选出总体积不超过j的价值

若选,最后一个已经确定,前面的1~i-1随便选。那就先从1~i-1中选出体积不超过j-w[i]的物品。最后在加上最后一个物品的价值 ,表示f[i-1][j-w[i]]+v[i]。

其中,选的条件是体积大于要选第i个物品的体积,因此需要加一个判断条件

最终最大值即是f[n][vv]前n个物品


    2、完全背包

有 N种物品和一个容量是 V 的背包,每种物品都有无限件可用

第 i 种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

DP算法-背包问题与线性DP问题(Acwing)_第3张图片        

 f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i],f[i-1][j-2*w[i]]+2*v[i],f[i-1][j-3*w[i]]+3*v[i],....,f[i-1][j-k*v[i]]+k*w[i])

(选0个f[i-1,j] 选1个f[i-1][j-w[i]]+v[i]..........选k个)

 f[i][j-w]=max(       f[i-1][j-w[i]],      f[i-1][j-2*w[i]]+v[i]     f[i-1][j-3*w[i]]+2*v[i],....................)

替代后发现 f[i][j]=max(f[i-1][j],f[i][j-w[i]]+v[i]);

for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=vv;j++)
		{
			if(j>=w[i])
			{
				f[i][j]=max(f[i-1][j],f[i][j-w[i]]+v[i]);
			}
			else{
				f[i][j]=f[i-1][j];
			}
		}
	}

    3、多重背包

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

DP算法-背包问题与线性DP问题(Acwing)_第4张图片

for(int i=1;i<=n;i++)
	{
		cin>>v[i]>>w[i]>>s[i];
		for(int j=1;j<=vv;j++)
		{
			for(int k=0;k<=s[i];k++){
				if(j>=k*v[i]){
					f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);
				}
			}
		}
	}

 多重背包问题需要注意的是它每件物品都有数量限制,最多s[i]件,但是没见物品可以重复拿,所以需要嵌套一个循环从k=0到k=s[i]


三、线性dp问题

  1、数字三角形问题

给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。

        7
      3   8
    8   1   0
  2   7   4   4
4   5   2   6   5

 注意:为什么要从下向上考虑,应为如果从上到下考虑有边界特殊情况,如果从下向上,就没有   边界情况,最大值即为f[1][1],首先还需要将三角形变形到二维数组中DP算法-背包问题与线性DP问题(Acwing)_第5张图片

for(int i=1;i<=n;i++)
{
    f[n][i]=w[n][i];   //从下向上 最后一行初始化 
}
for(int i=n-1;i>=1;i--)
{
    for(int j=1;j<=i;j++)
    {
        f[i][j]=max(f[i+1][j]+w[i][j],f[i+1][j+1]+w[i][j]);
    }
}

总结:首先是初始化,因为从第n-1行向上,所以要初始化第n行数据

	f[n][i]=a[n][i];

注意顺序问题,是a[i][j]赋值给f[n][i],如果顺序对调则错误

注意这里求的是最大长度,所以f[i+1][j]+w[i][j](加上该点的值)


2、最长上升子序列

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

DP算法-背包问题与线性DP问题(Acwing)_第6张图片

for(int i=1;i<=n;i++){
		//初始化 
		f[i]=1;
		for(int j=1;j

总结:首先初始化f[i]=1,应为只有一个数连续长度也是1.再遍历j,从1到i,如果满足a[j]判断倒数第二个数),最后因为不能认为到最后一个数他的长度最大即为f[n],f[i]表示的是任意第i个数结尾,所以还要进行max比较


3、最长上升子序列Ⅱ

给定一个长度为 N 的数列,求数值严格单调递增的子序列的长度最长是多少。

数据范围

1≤N≤100000,
−109≤数列中的数≤109

输入样例:

7
3 1 2 1 8 5 6

输出样例:

4

此题难点在于N最大给到了100000 如果用双重循环会超时

int main(){
	int n;
	cin>>n;
	vectorarr(n);
	for(int i=0;i>arr[i];
	}
	vectorstk;
	stk.push_back(arr[0]);
	for(int i=1;istk.back()){
			stk.push_back(arr[i]);
		} 
		else{
			*lower_bound(stk.begin(),stk.end(),arr[i])=arr[i];
			
		}
	}
	cout<

总结:此题我运用的是栈函数,vector()容器可以运用很多内置函数

stk.push_back()栈中添加元素,

stk.back()取栈顶元素

*lower_bound(stk.begin(),stk.end(),arr[i])=arr[i] 替换从头开始大于或等于该元素的值

最后只需要输出栈的长度即可得栈得最大长度


4、最长公共子序列

给定两个长度分别为 N 和 M 的字符串A和 B,求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。

输入样例:

4 5
acbd
abedc

输出样例:

3

DP算法-背包问题与线性DP问题(Acwing)_第7张图片

for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			f[i][j]=max(f[i-1][j],f[i][j-1]);
			if(a[i]==b[j]){
				f[i][j]=max(f[i][j],f[i-1][j-1]+1);
			}
		}
	}

 总结:此题关键是找出状态表示f[i][j]表示a[1-i]b[1-j]得最长公共子序列长度集合a[i],b[j]作为最后得元素各有两种情况是选和不选所以分为4种情况00 01 10 11(当a[i]=b[j]时)


5、最短编辑距离 

给定两个字符串 A 和 B,现在要将 A 经过若干操作变为 B,可进行的操作有:

  1. 删除–将字符串 A 中的某个字符删除。
  2. 插入–在字符串 A 的某个位置插入某个字符。
  3. 替换–将字符串 A 中的某个字符替换为另一个字符。

现在请你求出,将 A 变为 B 至少需要进行多少次操作。

输入样例:

10 
AGTCTGACGC
11 
AGTAAGTAGGC

输出样例:

4

 DP算法-背包问题与线性DP问题(Acwing)_第8张图片

for(int i=0;i<=m;i++) f[0][i]=i; //a前0个字母想要匹配b前m 添加操作 
	for(int j=0;j<=n;j++) f[j][0]=j;//b前0个字母想要匹配a前 n 删除操作
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			f[i][j]=min(f[i-1][j]+1,f[i][j-1]+1);
			if(a[i]==b[j]){
				f[i][j]=min(f[i][j],f[i-1][j-1]); 
			}
			else{
				f[i][j]=min(f[i][j],f[i-1][j-1]+1);
			}
		}
	}  

 总结:此题需要进行初始化f[i][0]=i(表示删除操作,删除的次数就是i的个数)同理f[0][j]=j(表示插入操作),在替换部分需要考虑两种情况,首先时最后两个相等时,操作次数就等于f[i-1][j-1]。不相等时需要加上1


四、解题总结

        首先确定题目是否需要用dp思路分析 经典问题就包括两类背包问题还有求公共子序列

        其次确定f[i][j]状态表示,二维的化一般表示为求a[1~i]b[1~j]的具体操作

        然后分析各个状态的状态表示方程,一般运用不重不漏原则,或者看最后一个状态选择或者            不选择,分多种情况考虑,f[i][j]即求每个状态的最大值

        最后确定最大值,有的是遍历到末尾即为最大值,例如背包问题都是f[n][vv],但是lsc问题是每种情况出循环就取最大值,应为lsc的状态表示意思是以第i个数结尾的上升子序列长度,不一定到末尾才是最大值

        注意点是:f[i][j]初始化的情况需要考虑

你可能感兴趣的:(动态规划,算法)