1,动态规划(DP) 记忆化搜索 实质上是以空间换时间
最优子结构
无后效性
子问题重叠性 ---> 记忆化搜索
2,dp思想
开个数组存
计算前先看计算过没,计算过了直接返回结果
3,记忆化搜索 求解斐波纳契数列
#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;
}
memset,只能初始化0或者-1,fill初始化
memset(dp,-1/0,sizeof dp);
fill (array,array+4,5); // myvector: 5 5 5 5 0 0 0 0
fill (array+3,array+6,8); // myvector: 5 5 5 8 8 8 0 0
3,数塔模型 记忆化搜索
#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;
}
4,数塔问题 递推解法
#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;
}
4,最大子段和 一段数最大的和 aL-aR
b[i]以i结尾最大的的子段和
max(a[i]+b[i],a[i]); +b[i]<a[i] 就不用接了
求具体方案
从c[i] (以i为结尾的最大子段和的起始点) 不接 起点是c[i]-1 接 起点是从c[i];
5,序列组合
n个格子,每个格子可填写数[1,m].满足任意一对相邻的格子的数绝对值小于等于2的填写方案数
#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;
}
递推版本
#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()
{
memset(dp , -1 , sizeof dp);
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;
}
6,LIS(最长上升子序列)
O(n^2)
dp[i]=max(a[j]+1); dp[j]<dp[i]
#include <iostream>
using namespace std;
int a[105],dp[105];
int main()
{
int n;
cin>>n;
fill(dp,dp+n,1);//初始化数组为1
for(int i=1;i<=n;i++){
cin>>a[i];
}
//dp[1]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
if(a[i]>a[j]){
dp[i]=max(dp[i],dp[j]+1);
}
}
}
for(int i=1;i<=n;i++){
cout<<dp[i]<<" ";
}
return 0;
}
7,走格子
8最长公共子序列
dp[i][j]是第一个字符串的前i个字符与第二个字符串前j个字符的最长公共子序列的长度
x==y dp[i][j]=dp[i-1][j-1]+1
x!=y dp[i][j]=max(dp[i-1][j] dp[i][j-1])
8,回文串模型
给你一个字符串长度为2000 Q(Q<=1e6)次询问 [L,R]子串是否为回文串
dp[L][R]=dp[L+1][R-1]&&s[L]==s[R];
dp[i][j]表示区间L-R是否为回文串
#include <bits/stdc++.h>
using namespace std;
int dp[2000][2000];
int main()
{
string str;
cin>>str;
str='#'+str;
for(int i=str.size();i>0;i--){
for(int j=i;j<=str.size();j++){
//i-j==0 "a"
//i-j==1 "aa"
//i-j==2 "aba"
//前提是str[i]==str[j]
dp[i][j]=(str[i]==str[j])&&(i-j<=2||dp[i+1][j-1]);
}
}
int q;
cin>>q;
while(q--){
int x,y;
cin>>x>>y;
if(dp[x][y]){
cout<<"yes"<<endl;
}else{
cout<<"no"<<endl;
}
}
return 0;
}
/*-------------------------------*/
求最长回文子序列
dp[i][j] i表示头 j表示尾 头尾最长的回文子序列
s[i]==s[j] dp[i][j]=dp[i+1][j-1]+1
s[i]!=s[j] dp[i][j]=max(dp[i+1][j],dp[i][j-1])
#include<bits/stdc++.h>
using namespace std;
int dp[1300][1300];
int main(){
string s;
cin>>s;
int n=s.size();
for(int i=0;i<n;i++){
if(s[i]>='A'&&s[i]<='Z'){
s[i]=s[i]+32;
}
}
s="*"+s;
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=n;j>=1;j--){
if(s[i]==s[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][1]<<endl;
return 0;
}
9,背包模型
n个物品 体积v,价值w(n,m<1000,w<=1e9)
取n个物品(不超过体积的前提下)使价值最大
dfs(st,v)
max(dfs(st+1,v) w[st]+dfs(st+1,v-v[st]);