算法知识-从递归入手三维动态规划

一.基础

尝试函数有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;
    }
相关推荐
chipsense1 天前
霍尔电流传感器选型方法论再升级:从800V平台到TMR竞争的全场景决策树
算法·决策树·机器学习·闭环霍尔·tmr传感
CoderYanger1 天前
14届蓝桥杯省赛Java A 组Q1~Q3
java·开发语言·线性代数·算法·职场和发展·蓝桥杯
会编程的土豆1 天前
【日常做题】 代码随想录(岛屿最大面积+寻宝)
数据结构·算法·图论
阿洛学长1 天前
汉洛塔结构思维
算法
木子n11 天前
第2篇:坐标变换与数学基础:FOC算法的核心数学工具
算法·电机控制·foc
阿Y加油吧1 天前
两道经典 DP 题:零钱兑换 & 单词拆分(完全背包 + 字符串 DP)
算法
疯狂打码的少年1 天前
有序线性表删除一个元素:顺序存储 vs 单链表,平均要移动多少个元素?
数据结构·算法·链表
y = xⁿ1 天前
20天速通LeetCode day07:前缀和
数据结构·算法·leetcode
载数而行5201 天前
算法集训1:模拟,枚举,错误分析,前缀和,差分
算法
hehelm1 天前
vector模拟实现
前端·javascript·算法