一.基础
尝试函数有1个可变参数可以决定返回值,进而可以改出1维动态规划表的表现
尝试函数有2个可变参数可以决定返回值,进而可以改出2维动态规划表的表现
尝试函数有3个可变参数可以决定返回值,进而可以改出3维动态规划表的表现
大体过程都是:
写出尝试递归
记忆化搜索
严格位置依赖的动态规划
空间,时间的更多优化
二.例题
1.题目一

首先分析状态:f(i,m,n):表示[i...n-1]自由选择,保证0个数不超过m个,1的个数不超过n个,最多选择多少个字符串
basecase:i==s.size(),此时已经选择到了最末尾,能选择0个字符串
决策方案:
(1)不选当前位置:f(i+1,m,n)
(2)选当前位置:f(i+1,m-zero,n-one)
(1)记忆化搜索
cpp
void get_zo(string &s,int &zero,int &one){
for(auto&c:s){
if(c=='0')zero++;
if(c=='1')one++;
}
}
//f(i,m,n):表示[i...n-1]自由选择,保证0的个数不超过m,1的个数不超过n
//最多能选择多少个字符串
//决策方案:当前位置选与不选
int f(int i,int m,int n,vector<string>&strs,vector<vector<vector<int>>>&dp){
if(i==strs.size()){
return 0;
}
if(dp[i][m][n]!=-1){
return dp[i][m][n];
}
int zero=0,one=0;
get_zo(strs[i],zero,one);
int ans;
//决策方案1:不选当前位置
ans=f(i+1,m,n,strs,dp);
//决策方案2:选当前位置
if(zero<=m&&one<=n){
ans=max(ans,f(i+1,m-zero,n-one,strs,dp)+1);
}
dp[i][m][n]=ans;
return ans;
}
(2)严格位置依赖的动态规划

通过画图分析发现,每个位置只依赖它的上一层元素,所以我们只需要从上往下枚举,剩下两层循环顺序无所谓
cpp
int f1(vector<string>& strs, int m, int n){
int len=strs.size();
vector<vector<vector<int>>>dp(len+1,vector<vector<int>>(m+1,vector<int>(n+1,0)));
for(int i=len-1;i>=0;i--){
for(int j=0;j<=m;j++){
for(int k=0;k<=n;k++){
int zero=0,one=0;
get_zo(strs[i],zero,one);
//决策方案1:不选当前位置
dp[i][j][k]=dp[i+1][j][k];
//决策方案2:选当前位置
if(zero<=j&&one<=k){
dp[i][j][k]=max(dp[i][j][k],dp[i+1][j-zero][k-one]+1);
}
}
}
}
return dp[0][m][n];
}
(3)空间压缩
由于我们刚才分析了,每个位置只依赖它的上一层元素,所以我们只需要维护两个二维数组(一个数组作为它的上一层,一个数组作为当前层),让它滚动下去即可
cpp
int f2(vector<string>& strs, int m, int n){
int len=strs.size();
vector<vector<int>>last(m+1,vector<int>(n+1,0)),
cur(m+1,vector<int>(n+1,0));
for(int i=len-1;i>=0;i--){
for(int j=0;j<=m;j++){
for(int k=0;k<=n;k++){
int zero=0,one=0;
get_zo(strs[i],zero,one);
//决策方案1:不选当前位置
cur[j][k]=last[j][k];
//决策方案2:选当前位置
if(zero<=j&&one<=k){
cur[j][k]=max(cur[j][k],last[j-zero][k-one]+1);
}
}
}
last=cur;
}
return cur[m][n];
}
2.题目二
状态:f(i,r,p):表示从[i,...n-1]:还剩余r名员工,还差p个利润,能有多少种选择
决策方案:
(1)不选则当前工作,ans=f(i+1,r,p)
(2)选择当前工作,在r-group[i]>=0情况下,ans=ans+f(i+1,r-group[i],max(0,p-porfit[i]))
注意这里有p-porfit[i]和0取大,因为我们尽量不希望我们的状态小于零,如果小于零和等于零效果一样的话,因为我们后续需要改严格位置依赖的动态规划
(1)记忆化搜索
cpp
int f(int i,int r,int p,vector<int>&group,vector<int>&profit,vector<vector<vector<int>>>&dp){
//如果选到末尾了
if(i==group.size()){
return p==0?1:0;
}
if(dp[i][r][p]!=-1){
return dp[i][r][p];
}
//决策方案:
//(1)不选择
int ans=0;
ans=(ans+f(i+1,r,p,group,profit,dp))%mod;
//(2)选择
if(r-group[i]>=0){
//注意我们这里尽量不想让可变参数变为负数,如果零和负数效果一样的话
ans=(ans+f(i+1,r-group[i],max(0,p-profit[i]),group,profit,dp))%mod;
}
dp[i][r][p]=ans;
return ans%mod;
}
(2)严格位置依赖的动态规划
通过分析发现,每一层还是仅仅依赖它的上一层,所以依旧是从上往下枚举,其他两层循环随意
cpp
int f1(int n, int minProfit, vector<int>& group, vector<int>& profit ){
int len=group.size();
vector<vector<vector<int>>>dp(len+1,vector<vector<int>>(n+1,vector<int>(minProfit+1,0)));
for(int r=0;r<=n;r++){
dp[len][r][0]=1;
}
for(int i=len-1;i>=0;i--){
for(int j=0;j<=n;j++){
for(int k=0;k<=minProfit;k++){
//决策方案:
//(1)不选择
dp[i][j][k]=dp[i+1][j][k];
//(2)选择
if(j-group[i]>=0){
//注意我们这里尽量不想让可变参数变为负数,如果零和负数效果一样的话
dp[i][j][k]=(dp[i][j][k]+dp[i+1][j-group[i]][max(k-profit[i],0)])%mod;
}
}
}
}
return dp[0][n][minProfit];
}
(3)空间压缩
和上一题思路一致
cpp
int f2(int n, int minProfit, vector<int>& group, vector<int>& profit ){
int len=group.size();
vector<vector<int>>last(n+1,vector<int>(minProfit+1,0)),cur(n+1,vector<int>(minProfit+1,0));
for(int r=0;r<=n;r++){
last[r][0]=1;
}
for(int i=len-1;i>=0;i--){
for(int j=0;j<=n;j++){
for(int k=0;k<=minProfit;k++){
//决策方案:
//(1)不选择
cur[j][k]=last[j][k];
//(2)选择
if(j-group[i]>=0){
//注意我们这里尽量不想让可变参数变为负数,如果零和负数效果一样的话
cur[j][k]=(cur[j][k]+last[j-group[i]][max(k-profit[i],0)])%mod;
}
}
}
last=cur;
}
return cur[n][minProfit];
}
3.题目三

状态:f(i,j,k):表示当前来到i,j位置,还剩k步,还留在棋盘上的概率
决策方案:向八个方向转移,每个方向概率/8,之后在累加
basecase:如果越界,概率为0,没越界,之后k为0,概率为1
cpp
vector<pair<int,int>>dir={{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};
double f(int i,int j,int k,int n,vector<vector<vector<double>>>&dp){
//basecase:
if(i<0||j<0||i>=n||j>=n){
return 0;
}
if(k==0){
return 1;
}
if(dp[i][j][k]!=-1){
return dp[i][j][k];
}
double ans=0;
for(auto&[dx,dy]:dir){
ans+=f(i+dx,j+dy,k-1,n,dp)/8;
}
dp[i][j][k]=ans;
return ans;
}
