概述
动态规划
ps:最优子结构,无后效性,子问题的重叠性>记忆化搜索
斐波纳契列模型
- 开数组存结果
- 计算之前先看是否计算过了,如果算过了直接返回结果
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
int dp[maxn];
// 记忆化搜索的基本结构
// 要怎么表示没被算过?
int f (int x)
{
// 算过了就不算了, 记忆化搜索的关键,一定要放在dfs的开头!!!!
if (dp[x] != -1) return dp[x];
if (x == 1 || x == 2) return 1;
int ans = f(x - 1) + f(x - 2);
dp[x] = ans;
return ans;
}
int main()
{
int n; cin >> n;
// 把所有dp置为-1
// memset 不是什么值都可以赋的!
// 只能初始化0 || -1
memset(dp , -1 , sizeof dp);
cout << f(n) << endl;
return 0;
}
数塔问题模型
1.记忆化搜索
//数塔问题
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
int dp[maxn][maxn] , a[maxn][maxn];
int n , m;
// dfs的作用:求从x,y到底部的路径最大值
int dfs (int x , int y)
{
// 边界条件
// 不合法的状态,我们要让他一定不被考虑。
// 如果全部都是负数,那么返回0将是错误的结果
if (y > x) return -inf;
if (dp[x][y] != -1) return dp[x][y];
if (x == n + 1) return 0;
int ans = a[x][y] + max (dfs(x + 1 , y) , dfs(x + 1 , y + 1));
dp[x][y] = ans;
return ans;
}
int main()
{
// 把所有dp置为-1
// memset 不是什么值都可以赋的!
// 只能初始化0 || -1
memset(dp , -1 , sizeof dp);
cin >> n >> m;
for (int i = 1 ; i <= n; i ++){
for (int j = 1 ; j <= i ; j++){
cin >> a[i][j];
}
}
cout << dfs(1 , 1) << endl;
return 0;
}
2.递推
//数塔问题=递推版本:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
// dp(x , y) 从(x , y)到最下面一层的最大值
int dp[maxn][maxn] , a[maxn][maxn];
int main()
{
int t; cin >> t;
while (t--){
// 初始化
memset (dp , 0 , sizeof dp);
int n; cin >> n;
for (int i = 1 ; i <= n ; i++){
for (int j = 1 ; j <= i ; j++){
cin >> a[i][j];
}
}
for (int i = n ; i >= 1 ; i--){
for (int j = 1 ; j <= i ; j++){
dp[i][j] = a[i][j] + max(dp[i + 1][j] , dp[i + 1][j + 1]);
}
}
cout << dp[1][1] << endl;
}
return 0;
}
最大子段和模型
ps:一段数最大的和
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10005;
int a[maxn],b[maxn],c[maxn];
int n;
int main()
{
cin >> n;
for(int i = 1 ; i <= n ; i++){
cin >> a[i];
}
b[1] = a[1];
c[1] = 1;
for(int i = 2 ;i <= n ; i++){
b[i] = a[i]+max(b[i-1],0);//b[i]以i为结尾的最大子段和
if(b[i]==a[i]+b[i-1])//如果选择
c[i]=c[i-1];//c[i]以i结尾的最大子段和的起始点
else//如果不选
c[i]=i;
}
int u,s = 0;
for(int i = 1 ; i <= n ; i++){
if(s<b[i]){
s=b[i];
u=i;
}
}
for(int i = c[u] ; i <= u ; i++)
cout << a[i] <<" ";
cout <<endl << s << endl;
return 0;
}
序列组合模型
ps:n个格子,每个格子可填写数[1,m].满足任意一对相邻的格子的数绝对值小于等于2的填写方案数
1.记忆化搜索
//dfs版本
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
int n , m;
// dfs (st , val) 求从st填写到1格子的所有可能方案.
int dp[105][108];
int dfs (int st , int val)
{
if (dp[st][val] != -1) return dp[st][val];
if (st == 0) return 1;
int ans = 0;
for (int i = 1 ; i <= m ; i++){
if (abs(val - i) <= 2){
ans += dfs(st - 1 , i);
}
}
dp[st][val] = ans;
return ans;
}
int main()
{
memset(dp , -1 , sizeof dp);
cin >> n >> m;
int ans = 0;
for (int i = 1 ; i <= m ; i++){
ans += dfs (n - 1 , i);
}
cout << ans << endl;
return 0;
}
2.递推
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
int n , m;
// dp(i , j) 代表从1号格子填写到第i号格子,且第i号格子的数为j的所有方案数.
int dp[maxn][maxn];
int main()
{
int n , m; cin >> n >> m;
// 边界条件,因为1之前没有格子了
for (int i = 1 ; i <= m ; i++)
dp[1][i] = 1;
// 从2开始转移.
for (int i = 2 ; i <= n ; i++){
for (int j = 1 ; j <= m ; j++){
for (int k = max(j - 2 , 1) ; k <= min(j + 2 , m) ; k++){
dp[i][j] += dp[i - 1][k];
}
}
}
int ans = 0;
for (int i = 1 ; i <= m ; i++){
ans += dp[n][i];
}
cout << ans << endl;
return 0;
}
LIS:最长上升子序列模型
#include <bits/stdc++.h>
using namespace std;
int dp[100];
int main()
{
string a;
cin >> a;
int ans;
dp[0]=1;
for(int i=1;i<a.size();i++){
for(int j=i-1;j>=0;j--){ // j==[1,i-1] a[j]<a[i] O(n^2)
if(a[j]<a[i]){
dp[i] = max(dp[i],dp[j]+1);
}
}
ans =max(ans,dp[i]);
}
cout << ans;
}
走格子模型
ps:只能走下和右
//dp记录到该格子有多少种方案
#include <bits/stdc++.h>
using namespace std;
int a[100][100];
int dp[100][100];
int main()
{
int n ,m;
cin >> n >> m;
int n1,m1;
cin >> n1 >>m1;
for(int i = m ;i <= m1 ; i++)
dp[n][i] = 1;
for(int i =n+1 ; i <= n1 ;i++){
for(int j = m ;j <= m1 ;j++){
dp[i][j]=dp[i][j-1]+dp[i-1][j];
}
}
cout << dp[n1][m1];
return 0;
}
LCS:最长公共子序列模型
ps:求两个字符串最长公共序列
#include <bits/stdc++.h>
using namespace std;
int dp[1005][1005];
int main()
{
string a,b;
int n,m;
cin >> n >>m;
cin >> a;
cin >> b;
a='#'+a;
b='^'+b;
for(int i = 1 ; i <=n ; i++ ){
for(int j = 1 ; j <=m ; j++ ){
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] );
}
}
}
cout << dp[n][m];
return 0;
}
回文串模型
1.给你一个字符串。长度为2000,Q(Q<=1e6)次询问[L,R]子串是否为回文串
if(s[l]==s[r])
dp[l][r] = dp[l+1][r-1]; //O(n^2)
2.最长回文序列
//dp存最长回文序列数 a为回文数组 i为数组头 j为数组尾
if(a[i]==a[j])
dp[i][j]=dp[i+1][j-1]+1;
else
dp[i][j]=max(dp[i+1][j],dp[i][j+1]);
背包模型
ps:造出n物品在不超过体积V的情况下,让价值(w)最大(v<=1000,n<=1000,w<=1e9)
//dfs加记忆化搜索
int dfs(st,v,w){ // st第几个 v体积 w为价值
max(dfs(st+1,v),w[st]+dfs(st+1,v-v[st]));//返回最大价值
}
最后
以上就是无私白羊为你收集整理的学习笔记-数据结构与算法 dp模型 (动态规划)的全部内容,希望文章能够帮你解决学习笔记-数据结构与算法 dp模型 (动态规划)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复