你必须了解的动态规划算法


动态规划

动态规划是分治思想的延伸,大事化小小事化无
动态规划具备的特点:
1.把原来的问题分解成几个相似的子问题
2.所有的子问题都只需要解决一次
3.存储子问题的解

动态规划的本质

动态规划的本质,是对问题状态的定义状态转移方程的定义
从以下角度考虑:
1.状态定义(定义的状态一定要形成递推关系)
2.状态间的转移方程定义
3.状态的初始化
4.返回结果

动态规划使用场景

最大值/最小值,可不可行,是不是,方案个数

例题1:斐波那契数列

LeetCode:斐波那契数列

分析问题过程:
问题:数列第N项的值
状态F(i):数列第i项的值
转移方程:F(i)=F(i-1)+F(i-2)
初始状态:F(0)=0,F(1)=1
返回:F(n)

class Solution {
    
    
public:
    //递归
    int fib(int n) {
    
    
        if(n<=1){
    
    
            return n;
        }
        if(n==2){
    
    
            return 1;
        }
        return fib(n-1)%1000000007+fib(n-2)%1000000007;
    }
    //动态规划-记录每个子问题的解
    int fib(int n){
    
    
        if(n==0){
    
    
            return 0;
        }
        vector<int> v(n+1,0);
        v[1]=1;//初使状态
        for(int i=2;i<=n;i++){
    
    
            v[i]=v[i-1]%1000000007+v[i-2]%1000000007;//转移方程
        }
        return v[n]%1000000007;
    }
    //动态规划-不记录每个子问题解
    int fib(int n){
    
    
        if(n<=1){
    
    
            return n;
        }
        int a=0;
        int b=1;
        int num;
        for(int i=2;i<=n;i++){
    
    
            num=a%1000000007+b%1000000007;
            a=b;
            b=num;
        }
        return num%1000000007;
    }
};

例题2. 变态跳台阶

牛客:变态跳台阶

问题:跳上n级台阶的方法个数
状态F(i):跳上i级台阶的方法个数
转移方程:F(i)=F(0)+F(1)+F(2)+…+F(i-1);
F(i-1):F(i-2)+F(i-3)+…+F(0);
F(i)=F(i-1)+F(i-1)=2*F(i-1);
初使状态:F(1)=1
返回:F(n)

class Solution {
    
    
public:
    int jumpFloorII(int number) {
    
    
        if(number==0)
            return 0;
        int ret=1;
        for(int i=2;i<=number;i++){
    
    
            ret=ret*2;//转移方程
        }
        return ret;
    }
};

变形1:一次只能跳1级或者2级(类似于斐波那契数列)

状态转移方程:F(i)=F(i-1)+F(i-2)

变形2:矩形覆盖

牛客:矩形覆盖

状态转移方程:F(i)=F(i-1)+F(i-2)

class Solution {
    
    
public:
    int rectCover(int number) {
    
    
        if(number<=1){
    
    
            return number;
        }
        int a=1;
        int b=1;
        int ret;
        for(int i=2;i<=number;i++){
    
    
            ret=a+b;
            a=b;
            b=ret;
        }
        return ret;
    }
};

例题3:最大连续子数组和

牛客:最大连续子数组和
在这里插入图片描述

问题:数组的最大连续和
状态:F(i)
转移方程:F(i)=max(F(i-1)+a[i],a[i])
初使状态F(0)=a[0]
返回值:max(F(i))

class Solution {
    
    
public:
    int FindGreatestSumOfSubArray(vector<int> array) {
    
    
        if(array.empty()){
    
    
            return 0;
        }
        vector<int> v(array.size(),0);
        v[0]=array[0];
        int ret=v[0];
        for(int i=1;i<array.size();i++){
    
    
            v[i]=max(v[i-1]+array[i],array[i]);//状态转移方程
            ret=max(ret,v[i]);
        }
        return ret;
    }
};

例题4:拆分词句

问题:单词是否可以成功分割
状态:F(i)
转移方程:F(i):j<i&&[j+1,i]是否可以在词典中找到
返回结果:F(s.size())

class Solution {
    
    
public:
	/*bool wordBreak(string s, unordered_set<string> &dict) {
		vector<bool> v(s.size() + 1);
		for (int i = 1; i <= s.size(); i++){
			if (dict.find(s.substr(0, i)) != dict.end()){
				v[i] = true;
				continue;
			}
			for (int j = i - 1; j>0; j--){
				if (v[j] && (dict.find(s.substr(j, i)) != dict.end())){
					v[i] = true;
					break;
				}
			}
		}
		return v[s.size()];
	}*/
	bool wordBreak(string s, unordered_set<string> &dict) {
    
    
		vector<bool> v(s.size() + 1);
		v[0] = true;
		for (int i = 1; i <= s.size(); i++){
    
    
			for (int j = i - 1; j>=0; j--){
    
    
				if (v[j] && (dict.find(s.substr(j, i)) != dict.end())){
    
    
					v[i] = true;
					break;
				}
			}
		}
		return v[s.size()];
	}
};

例题5.三角矩阵

牛客:三角矩阵

问题:从(0,0)到达最后一行的最小路径和
状态F(i,j):从(0,0)到达(i,j)的最小路径和
F(i,j):min(F(i-1,j),F(i-1,j-1))+a[i][j]
每一行第一列,最后一列只有一条路到达
F(i,0):F(i-1,0)+a[i][0]
F(i,i):F(i-1,i-1)+a[i][i]
初使状态:F(0,0)=a[0][0]
返回结果:max{F(row-1,j)}

class Solution {
    
    
public:
    int minimumTotal(vector<vector<int> > &triangle) {
    
    
        if(triangle.empty()){
    
    
            return 0;
        }
        vector<vector<int>> vv(triangle);
        int row=triangle.size();
        int col=triangle[0].size();
        for(int i=1;i<row;i++){
    
    
            for(int j=0;j<i+1;j++){
    
    
                if(j==0){
    
    
                    vv[i][j]=vv[i-1][j]+triangle[i][j];
                }
                else if(j==i){
    
    
                    vv[i][j]=vv[i-1][j-1]+triangle[i][j];
                }
                else{
    
    
                    vv[i][j]=min(vv[i-1][j-1],vv[i-1][j])+triangle[i][j];
                }
            }
        }
        int row_vv=row-1;
        int ret=vv[row_vv][0];
        for(int i=0;i<vv[row_vv].size();i++){
    
    
            ret=min(ret,vv[row_vv][i]);
        }
        return ret;
    }
};

例题6.不同路径

LeetCode:不同路径

问题:从(0,0)到达(i,j)的路径和
状态F(i,j):从(0,0)到达(i,j)的路径个数
转移方程:F(i,j)=F(i-1,j)+F(i,j-1)
初使状态:F(0,0)=1
返回结果:F(i,j)

class Solution {
    
    
public:
    int uniquePaths(int m, int n) {
    
    
        vector<vector<int>> vv(m,vector<int>(n));
        for(int i=0,j=0;j<n;j++){
    
    
            vv[i][j]=1;
        }
        for(int i=0,j=0;i<m;i++){
    
    
            vv[i][j]=1;
        }
        for(int i=1;i<m;i++){
    
    
            for(int j=1;j<n;j++){
    
    
                if(i-1<0){
    
    
                     vv[i][j]=vv[i][j-1];
                }
                else if(j-1<0){
    
    
                    vv[i][j]=vv[i-1][j];
                }
                else{
    
    
                    vv[i][j]=vv[i-1][j]+vv[i][j-1];
                }
            }
        }
        return vv[m-1][n-1];
    }
};

例题7:不同路径2

LeetCode:不同路径II

状态F(i,j):从(0,0)到(i,j)路径个数
F(i,j):
if(obstacle[i][j]==1)
F(i,j)=0
else
F(i,j)=F(i,j-1)+F(i-1,j)
初使状态:
if(obstacle[i][0]==0&&obstacle[j][0]==0&&j<i)
F(i,0)=1;
if(obstacle[0][i]==0&&obstacle[0][j]==0&&j<i)
F(0,i)=1;

class Solution {
    
    
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
    
    
        if(obstacleGrid.empty()){
    
    
            return 0;
        }
        int row=obstacleGrid.size();
        int col=obstacleGrid[0].size();
        vector<vector<int>> vv(row,vector<int>(col,0));
        for(int i=0;i<row;i++){
    
    
            if(obstacleGrid[i][0]==0){
    
    
                vv[i][0]=1;
            }
            else{
    
    
                break;
            }
        }
        for(int i=0;i<col;i++){
    
    
            if(obstacleGrid[0][i]==0){
    
    
                vv[0][i]=1;
            }
            else{
    
    
                break;
            }
        }
        for(int i=1;i<row;i++){
    
    
            for(int j=1;j<col;j++){
    
    
                if(obstacleGrid[i][j]==1){
    
    
                    vv[i][j]=0;
                }else{
    
    
                vv[i][j]=vv[i-1][j]+vv[i][j-1];
                }
            }
        }
        return vv[row-1][col-1];
    }
};

例题8.带权值的最小路径和

牛客:带权值的最小路径和

状态F(i,j)
状态方程:F(i,j)=min(F(i-1,j),F(i,j-1))+F(i,j)
初使值:F(0,0)=grid[0][0]
返回值:F(row-1,col-1)

class Solution {
    
    
public:
    /**
     * 
     * @param grid int整型vector<vector<>> 
     * @return int整型
     */
    int minPathSum(vector<vector<int> >& grid) {
    
    
        // write code here
        int row=grid.size();
        int col=grid[0].size();
        vector<vector<int>> vv(grid);
        for(int i=1;i<row;i++){
    
    
            vv[i][0]=vv[i-1][0]+vv[i][0];
        }
        for(int j=1;j<col;j++){
    
    
            vv[0][j]=vv[0][j-1]+vv[0][j];
        }
        
        for(int i=1;i<row;i++){
    
    
            for(int j=1;j<col;j++){
    
    
                vv[i][j]=min(vv[i-1][j],vv[i][j-1])+vv[i][j];
            }
        }
        return vv[row-1][col-1];
    }
};

例题9.背包问题2

背包问题2
在这里插入图片描述

class Solution {
    
    
public:
    /**
     * @param m: An integer m denotes the size of a backpack
     * @param A: Given n items with size A[i]
     * @param V: Given n items with value V[i]
     * @return: The maximum value
     */
    int backPackII(int m, vector<int> &A, vector<int> &V) {
    
    
        // write your code here
        int n=A.size();
        if(n==0||m==0)
            return 0;
        vector<vector<int>> maxV(n+1,vector<int>(m+1,0));
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=m;++j){
    
    
                if(A[i-1]<=j){
    
    
                    maxV[i][j]=max(maxV[i-1][j],maxV[i-1][j-A[i-1]]+V[i-1]);
                }
                else{
    
    
                    maxV[i][j]=maxV[i-1][j];
                }
            }
        }
        return maxV[n][m];
    }
};

例题10.回文串分割

回文串分割在这里插入图片描述

class Solution {
    
    
public:
    /**
     * 
     * @param s string字符串 
     * @return int整型
     */
   bool ispal(string s,int start,int end){
    
    
        while(start<end){
    
    
            if(s[start]!=s[end])
            return false;
            ++start;
            --end;
        }
        return true;
    }
    int minCut(string s) {
    
    
        vector<int> v(s.size()+1);
        for(int i=1;i<=s.size();i++){
    
    
            v[i]=i-1;
        }
        for(int i=2;i<=s.size();i++){
    
    
            if(ispal(s,0,i-1)){
    
    
                v[i]=0;
                continue;
            }
            for(int j=1;j<i;j++){
    
    
                if(ispal(s,j,i-1)){
    
    
                    v[i]=min(v[i],v[j]+1);
                }
            }
        }
        return v[s.size()];
    }
};

例题11.编辑距离

编辑距离

在这里插入图片描述

问题:word1转成word2的最小操作次数
状态F(i,j):word1前i个字符转成Word2的前j个字符的最小操作次数
转移方程:F(i,j):min(F(i-1,j-1),F(i-1,j),F(i,j-1))+1

class Solution {
    
    
public:
    /**
     * 
     * @param word1 string字符串 
     * @param word2 string字符串 
     * @return int整型
     */
    int minDistance(string word1, string word2) {
    
    
        // write code here
        int len1=word1.length();
        int len2=word2.length();
        vector<vector<int>> minD(len1+1,vector<int>(len2+1));
        for(int i=0;i<=len2;++i)
            minD[0][i]=i;
        for(int i=0;i<=len1;++i){
    
    
            minD[i][0]=i;
        }
        for(int i=1;i<=len1;i++){
    
    
            for(int j=1;j<=len2;++j){
    
    
                minD[i][j]=min(minD[i][j-1],minD[i-1][j])+1;
                if(word1[i-1]==word2[j-1])
                    minD[i][j]=min(minD[i][j],minD[i-1][j-1]);
                else
                    minD[i][j]=min(minD[i][j],minD[i-1][j-1]+1);
                    
            }
        }
        return minD[len1][len2];
    }
};

猜你喜欢

转载自blog.csdn.net/Hedenghui777/article/details/115875264
今日推荐