(蓝桥杯C/C++)——动态规划(DP)

目录

一、线性DP

1.DP(动态规划)简介

2.动态规划的分析步骤

3.例题讲解

二、二维DP

1.二维DP简介

2.选数异或

三、最长上升子序列LIS

1.LIS简介

2.例题讲解

四、最长公共子序列LCS

1.最长公共子序列

2.最长公共子序列

2.求出具体子序列


一、线性DP

1.DP(动态规划)简介

DP(动态规划)全称DynamicProgramming,是运筹学的一个分支,是一种将复杂问题分解成很多重叠的子问题,并通过子问题的解得到整个问题的解的算法。

在动态规划中有一些概念:
状态 :就是形如dp[il[il=val的取值,其中i,i为下标,也是用于描述、确定状态所需的变量,val为状态值。
状态转移: 状态与状态之间的转移关系,一般可以表示为一个数学表达式,转移方向决迭代或递归方向。
**最终状态:**也就是题目所求的状态,最后的答案。

2.动态规划的分析步骤

1.确定状态,一般为"到第i个为止,xx为j(xx为k)的方案数/最小代价/最大价值",可以根据数据范围和复杂度来推理。

2.确定状态转移方程,即从已知状态得到新状态的方法,并确保按照这个方向一定可以正确躾渥黨瘛得到最终状态。

根据状态转移的方向来决定使用迭代法还是递归法(记忆化)

3.确定最终状态并输出。

3.例题讲解

破损的楼梯
问题描述

小孟来到了一座高耸的楼梯前,楼梯共有N级台阶,从第0级台阶出发。小孟每次可以迈上1级或2级台阶,但是,楼梯上的a级、第02级、第 a1级,以此奔推。共 M 级台阶的台阶面已经坏了,不能上去。

现在,小孟想要到达楼梯的顶端,也就是第N级台阶,但他不能踩到坏了的台阶上。请问有多少种不踩到坏了的台阶上到达顶端的方案数?

由于方案数很大,请输出其对1e9+7取模的结果

例题分析

设状态dpi表示走到第i级台阶的方案数。

状态转移方程为dpi=dpi-1+dpi-2,如

果i为破损的,则dp[il=0。

可以用一个桶来记录哪些位置是破损的。

从前往后更新,最后输出dpn

#include<bits/sldc++.h>

using namespace std;

const int N = 1e5+10,mod - 1e9+7;

int n,m,x,fN,visN;

signed main()

{

cin >> n >> m;

for(int i=l;i<=m;i ++)

{

cin >> x;

visx= 1;

}

f0= 1;

for(int i - 1;i < -n; i++)

{

if(visi)

continue;

fi]-fi -1+f\[i - 2;

fi %= mod;

}

cout << fn << '\n';

return 0;

}

二、二维DP

1.二维DP简介

二维DP就是指dp数组的维度为二维的dp(当然有时候可能会三维四维,或者存在一些优化使得它降维成一维),广义的来讲就是有多个维度的dp,即用于描述dp状态的变量不止一个。

2.选数异或

给定n个正整数ai,询问你其中有多少个不同子序列进行异或运算的值为x?

由于结果很大,你需要对998244353取模

异或运算:位远算的一种,符号为由,1^1=0,1^0=1,0^0=0.

子序列: 从切始序列中选出若于个数保持原有顺序的序列,

例题分析

设状态dpij表示到第i个数字为止(但不一定以第i个数字结尾),异或和为i的子序列个数。

对于第i层的状态,转移的方式有"选第i个"和"不选第i个"两种,转移方程为dpij= dpij + dpi - 1j ^ dpi-1\[j \^ a]。

最后结果就是dpnx

#include<bits/sldc++.h>

using namespace std;

using ll = long long;

const int N = 1e5+9;

const ll p = 998244353;

int aN,dpN70;

int main()

{

int n, x; cin >> n >> x;

dp 0 [0 = 1;

for(int i = 1; i <= n; ++ i)

{

for(int j = 0; j < 64; ++j)

{

dpij = (dpi-1j + dpi-1j \^ a\[i]) % p;

}

}

cout << dpnm << '\n';

return 0;

}

三、最长上升子序列LIS

1.LIS简介

LIS(最长上升子序列)是一个经典的DP模型。

子序列指的是一个序列中,按照原顺序选出若干个不一定连续的元素所组成的序列。这节课我们讲解O(n^2)时间复杂度的朴素LIS模型,LIS还有一种利用二分实现的O(nlogn)时间复杂度的模型,大家可以自行去学习,理解起来略有难度。在求解LIS时,一般我们会设dpi表示1~i序列中以ai结尾的最长上升子序列的长度,状态转移方程为:

dpi= max(dpj + 1),

if ai> aj

表示ai要插入到不同子序列后面的情况。

|----|---|---|---|---|---|---|---|---|
| a | 1 | 3 | 4 | 2 | 5 | 3 | 7 | 2 |
| dp | 1 | 2 | 3 | 2 | 4 | 3 | 5 | 2 |

2.例题讲解

小明是蓝桥王国的勇士,他晋升为蓝桥骑士,于是决定不断突破自我

这天蓝桥首席骑士长给他安排了 N 个对手,他们的战力值分别为....a1,a2,···an,且按顺序挡在小明的前方,对于这些对手小明可以选择单挑也可以选择群战

作为热血豪放的勇士,小明从不走回头路,且只愿意挑战战力值越来越强的对手

请你算算小明最多会挑战多少名对手

#include<bits/sldc++.h>

using namespace std;

const int N = 1e3+9;

int aN,dpN;

int main()

{

int n;

cin >> n;

for(int i = 1;i <= n; ++i)

cin >> ai;

for(int i = 1;i <= n; ++i)

{

dpi = 1;

for(int j =1;j < i; ++j)

{

if(ai > aj)

dpi = max(dpi, dpj + 1);

}

}

int ans = 0;

for (int i =1;i < n; ++i)

ans = max(ans, dpi);

cout << ans << '\n';

return 0;

}

四、最长公共子序列LCS

1.最长公共子序列

LCS(Longest Common Subsequence 最长公共子序列)是一个经典的DP模型。

这节课我们讲解O(n^2)时间复杂度的LCS模型。

LCS问题是给定两个序列A和B,求它们的最长公共子序列。

在求解LCS时,一般我们会设dpij表示A1\~i序列和B1\~j序列中(不规定结尾)的最长公共子序列的长度,状态转移方程为:

if ali]=bj:dp{i]j= dp{i-1]j-1+1

else dpij= max(dpi-1jl, dp\[ij-1);

解释一下:当ali]=bj时,可以将他们作为插入到LCS的后面,使得长度变长1,当ai!=b说明此时LCS不会延长,那就要从dp\[i-1j和dpij-1中取大的作为最长的长度。

|---|---|---|---|---|---|
| a | 1 | 3 | 4 | 2 | 5 |
| b | 1 | 4 | 3 | 5 | 2 |

|----|---|---|---|---|---|
| dp | 1 | 2 | 3 | 4 | 5 |
| 1 | 1 | 1 | 1 | | 1 |
| 2 | 1 | 1 | 2 | 2 | 2 |
| 3 | 1 | 2 | 2 | 2 | 2 |
| 4 | 1 | 2 | 2 | 2 | 3 |
| 5 | 1 | 2 | 2 | 3 | 3 |

|---|---|---|
| 1 | 3 | 2 |

2.最长公共子序列

给定一个长度为 N 数组 a 和一个长度为 M 的数组 b

请你求出它们的最长公共子序列长度为多少。

例题分析

设dpij表示序列a1\~i,b1\~j中最长公共子序列长度,

状态转移方程为:

if ali]=bj:dpij = dpi-1j-1+1

else: dp|i]j= max(dpi-1j, dpij-1);最后输出dpln]lm]即可。

#include <bits/stdc+.h>

using namespace std;

const int N = 1e3+9,

int n, m,aN, bN, dpMN;

int main()

{

int n, m;

cin >> n >> m;

for(int i = 1;i <= n; ++i)

cin >> ai;

for(int i= 1; j<= m; ++i)

cin >> bi;

for(int i = 1;i <= n; ++i)

for(int j = 1; j <= n; ++j)

{

if(ai == bj)

dpij = dpi - 1j - 1 + 1;

else dpij = max(dpi - 1j,dpi j - 1);

}

cout << dpnm << '\n';

return 0;

}

2.求出具体子序列

|---|---|---|---|---|---|
| a | 1 | 3 | 4 | 2 | 5 |
| b | 1 | 4 | 3 | 5 | 2 |

|----|---|---|---|---|---|
| dp | 1 | 2 | 3 | 4 | 5 |
| 1 | 1 | 1 | 1 | | 1 |
| 2 | 1 | 1 | 2 | 2 | 2 |
| 3 | 1 | 2 | 2 | 2 | 2 |
| 4 | 1 | 2 | 2 | 2 | 3 |
| 5 | 1 | 2 | 2 | 3 | 3 |

|---|---|---|
| 1 | 3 | 2 |

(i,j)从(n,m)往回走,当ai=bj时,往左上角走变为(i-1,j-1),此时找到了一个公共元素ai(或blj]),否则往左边或上边走(选择较大的方向)。

走到新位置后重复以上判断,直到走出边界

#include <bits/stdc+.h>

using namespace std;

using ll = long long;

const ll p= 1e9 + 7;

const int int = 1e9, N = 1e3 + 9;

int aN, bN, dpNN;

int main()

{

int n, m;

cin >> n >>m;

for(int i = 1; i <= n; ++i)

cin >> ai;

for(int i = 1; i <= m; ++i)

cin >> bi;

for(int i = 1; i <= n; ++i)

{

for(int j= 1; j<= m; ++ j)

{

if(ai == bj)

dpij = dpi - 1j - 1 + 1;

else dpij = max(dpij-1,dpi - 1j);

}

}

vector<int> v;

int x = n, y = n;

while(x && y)

{

if(ax == by)

{

v.push_back(ax);

x --, y--;

}

else (dpx - 1y > dpxy - 1)

x --;

else y--;

}

reverse(v.begin(),v.end());

for(const auto &i : v)

cout << i << ' ';

return 0;

}

相关推荐
Matrix_1111 小时前
手机里的计算摄影:广角形变校正算法
人工智能·算法·智能手机·计算摄影
WBluuue11 小时前
数据结构与算法:有序表(二):跳表
数据结构·c++·算法·skiplist
x1387028595712 小时前
c语言中srtlen(指针使用计算字符长度)、传值和传址调用
c语言·开发语言·算法·visual studio
海兰12 小时前
【实用程序】电商销售分析仪表盘 — 从零搭建一个AI参与的全栈数据洞察系统
人工智能·学习·算法
zwenqiyu13 小时前
P5283 [十二省联考 2019] 异或粽子题解
c++·学习·算法
wayz1113 小时前
Momentum:TSI(真实强度指数)技术指标详解
算法·金融·数据分析·量化交易·特征工程
万事大吉CC14 小时前
Python 笔试输入模板总结
python·算法
lihao lihao14 小时前
Linux信号
开发语言·c++·算法
大白话_NOI14 小时前
【洛谷 P2249】查找(深基 13. 例 1)+ 详细分析
c++·算法
吠品14 小时前
C++实现m行n列带边框的长方形输出
算法