一)斐波纳契
#include<bits/stdc++.h>
using namespace std;
int n;
int dp[1000];
int dpFun(int n){
if(dp[n])
return dp[n];
if(n==1||n==2)
return 1;
int ans=dpFun(n-1)+dpFun(n-2);
dp[n]=ans;
return ans;
}
int main()
{
cin >> n ;
cout << dpFun(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:求L到R最大的和
int a[100],b[100],c[100];
//a存数据,b[i]存以i结尾的最大子段和
b[i]=max(a[i]+b[i-1],a[i]);
//c存以i结尾的最大子段和的起始点
/*c[i]=c[i]+1;
c[i]=i;
*/
四)序列组合
1、所有相邻的数a[i]-a[i-1]的绝对值<=2
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;
}
递推方法
#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;
}
五)LIS:最长上升子序列
dp[i]=max(dp[j]+1); j属于[1,i-1]; dp[j]<dp[i]
O(n^2);
六)走格子
PS:从第一个点走到第二个点(只能向下走和向右走)
//dp[i][j]=dp[i][j+1]+dp[i+1][j];
#include <bits/stdc++.h>
using namespace std;
int bk[1000][1000];
int main(){
int n,m,Q;
cin>>n>>m;
for(int i=2;i<=m;i++)
bk[1][i]=bk[1][i-1]+1;
//先将最顶上的格子向右走完
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++)
bk[i][j]=bk[i-1][j]+1;
//再将向下走的格子走完
cin>>Q;
while(Q--){
//Q次询问<1,1>走到<x,y>需要多少步
int x,y;
cin>>x>>y;
cout<<bk[x][y]<<endl;
}
return 0;
}
七)LCS:最长公共子序列
PS:求两个字符串最长公共子串
第一个字符串的前i个字符与第二个字符串j个字符最长公共子序列的长度
if(x==y){
dp(i,j)=dp(i-1,j-1)+1;
}else{
dp(i,j)=max(dp(i-1,j),dp(i,j-1));
}
八)回文串模型
1、一个字符串长度为2000,Q次询问[L,R]子串是否为回文子串
dp(L,R)=dp(L+1,R-1)&&s[L]==s[R];
2、最长回文子序列
PS:字符串的第i个字符到第j个字符最长回文子串序列的长度
if(x==y){
dp(i,j)=dp(i+1,j-1)+1;
}else{
dp(i,j)=max(dp(i+1,j),dp(i,j-1));
}
背包模型
PS:一个体积V的背包,N个物品(x,y)x表示物品体积,y表示物品价值,不超过背包容器求价值最大化(V,x<=100),y<1e9
ST第几个物品 V体积和 W价值和
DFS(int ST,int V,int W);
dfs(int st,int v){
max(dfs(st+1,v),W[st]+dfs(st+1,v-V[st]));
W[st]表示st的价值,
}