剑指 Offer II 动态规划

088. 爬楼梯的最少成本

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n=cost.size();
        vector<int>f(n+1);
        f[0]=cost[0],f[1]=cost[1];

        //走到第i级台阶 最小代价
        //状态转移 从前一个或 前两个走上来
        cost.push_back(0);
        for(int i=2;i<=n;i++)
        {
            f[i]=cost[i]+min(f[i-1],f[i-2]);
        }
        return f[n];
    }
};

089. 房屋偷盗

class Solution {
public:
       /*
            f[i][0]
            f[i][1]
               偷还是不偷
            */
 int rob(vector<int>& nums) {
        int n=nums.size();
        vector<vector<int>>f(n+1,vector<int>(2));
        f[1][1]=nums[0];
        for(int i=2;i<=n;i++)
        {
            f[i][0]=max(f[i-1][0],f[i-1][1]);//没偷前一个咋样都无所谓
            f[i][1]=max(f[i-1][0],f[i-2][1]+nums[i-1]);
        } 
        return max(f[n][0],f[n][1]);
    }
};

090. 环形房屋偷盗

就两种情况 选1还是不选1

class Solution {
public:
       /*
            f[i][0]
            f[i][1]
               偷还是不偷
            */
 int rob(vector<int>& nums) {
        int n=nums.size();
        if(n==1)return nums[0];
        vector<vector<int>>f(n+1,vector<int>(2));
      //不选1 不初始化f[1][1]
        for(int i=2;i<=n;i++)
        {
            f[i][0]=max(f[i-1][0],f[i-1][1]);//没偷前一个咋样都无所谓
            f[i][1]=f[i-1][0]+nums[i-1];
        }    
         int ans= max(f[n][0],f[n][1]);
         f[1][1]=nums[0];
          for(int i=2;i<=n;i++)
        {
            f[i][0]=max(f[i-1][0],f[i-1][1]);//没偷前一个咋样都无所谓
            f[i][1]=f[i-1][0]+nums[i-1];
        }    
        ans=max(ans,f[n][0]);//选了1就别选n
        return ans;
    }
};

091. 粉刷房子

class Solution {
public:
/*
f[i][j]

j是颜色 从另外两个转移
*/
    int minCost(vector<vector<int>>& cost) {
        int n=cost.size();
        vector<vector<int>>f(n,vector<int>(3));
        f[0][0]=cost[0][0],f[0][1]=cost[0][1],f[0][2]=cost[0][2];

        for(int i=1;i<n;i++)
        {
            f[i][0]=cost[i][0]+min(f[i-1][1],f[i-1][2]);
             f[i][1]=cost[i][1]+min(f[i-1][0],f[i-1][2]);
              f[i][2]=cost[i][2]+min(f[i-1][1],f[i-1][0]);

        }
        return min(min(f[n-1][0],f[n-1][1]),f[n-1][2]);

    }
};

092. 翻转字符

前缀和

class Solution {
public:
/*
f[i] 1的前缀和

2e4
*/
    int minFlipsMonoIncr(string s) {
        int n=s.size();
        vector<int>f(n+1);
        for(int i=1;i<=n;i++)
        {
            f[i]=f[i-1]+ (s[i-1]=='1');
        //    cout<<f[i];

        }
        //puts("");
        int ans=min(f[n],n-f[n]);//全0 全1
         for(int i=1;i<=n;i++)
         {
             //把 i及以前变为0 以后变为1的代价 01111  00001
             int res=f[i]-f[1-1];//1~i的1变为0
             res+=(n-i)-(f[n]-f[i+1-1]);//i+1~n中0的个数;
          //   cout<<res<<endl;
             ans=min(ans,res);
         }
        return ans;

    }
};

093. 最长斐波那契数列

class Solution {
public:
/*
状态表示
f[i][j] 以arr[i]为结尾 arr[j]为arr[i]前一个元素组成的数列 的 最大值
状态转移
从f[j][k]
*/
    int lenLongestFibSubseq(vector<int>& arr) {
        int n=arr.size();
        vector<vector<int> >f(n+1,vector<int>(n+1));
        int res=0;
        unordered_map<int,int>pos;

        for(int i=0;i<n;i++)
        {
            pos[arr[i]]=i;//记录地址
            for(int j=0;j<i;j++)
            {
                f[i][j]=2;
                //找 j前面有没有x=arr[i]-arr[j]
                int x=arr[i]-arr[j];
                if(x<arr[j]&&pos.count(x)){
                    int k=pos[x];
                    f[i][j]=max(f[i][j],f[j][k]+1);
                }
                res=max(res,f[i][j]);
            }
        }
        return res<3?0:res;
    }
};

洞94 ?!最少回文分割 hard

class Solution {
public:
/*
f[i]状态表示  从1~i的方案  属性:最小值

阶段划分
可以分为1~i 2~i ... i~i

假设最后一步k~i转移
(1... k-1)  (k...i)

转移方程
f[i]=min(f[i],f[k-1]+1);

快速枚举i 到j是不是回文串 g[i][j]
1.s[i]==s[j]
2. j-i== 0、1 || g[i+1][j-1]  
*/
    int minCut(string s) {
        int n=s.size();
        s=' '+s;//单引号 方便 下标从1开始
        vector<int>f(n+1,1e9);
        vector<vector<bool>>g(n+1,vector<bool>(n+1));

        //初始化 st  第一次dp
        // i到j j先循环 保证g[i+1][j-1]先创建出来了 
        for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++){
            if(s[i]==s[j]){
                if(j-i<2||g[i+1][j-1])g[i][j]=true;
            }
        }
        f[0]=0;//0种方案
        for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++){//j==i 1~(i-1) i
        if(g[j][i]){//j前 i后
           f[i]=min(f[i],f[j-1]+1);
        }
        }
        return f[n]-1;
     }
};

095. 最长公共子序列

模板题 背过 不解释

class Solution {
public:
/*
状态表示 
f[i][j] a 1~i
        b 1~j
属性 max

阶段划分
两个字符串的位置(已处理的前缀长度)
*/
    int longestCommonSubsequence(string s1, string s2) {
        int n=s1.size(),m=s2.size();
        s1=' '+s1;s2=' '+s2;
        vector<vector<int>>f(n+1,vector<int>(m+1,0));
        f[n][0]=f[0][m]=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                f[i][j]=max(f[i-1][j],f[i][j-1]);
                if(s1[i]==s2[j])f[i][j]=max(f[i-1][j-1]+1,f[i][j]);
            }
        }
        return f[n][m];
    }
};

老美从来没看过的洞96?!字符串交织

class Solution {
public:
/*
f[i][j]
a 1~i
b 1~j 能组成c 1~i+j 

属性bool

阶段划分
i-1 j
i j-1 能组成1~i+j-1

转移方程
if(a[i]==c[i+j])f[i][j]|=f[i-1][j]

if(b[j]==c[i+j])f[i][j]|=f[i][j-1]


f[0][0]=true;
*/
    bool isInterleave(string a, string b, string c) {
        int n=a.size(),m=b.size();
        if(n+m!=c.size())return false;
        a=' '+a;b=' '+b;c=' '+c;
        vector<vector<bool>>f(n+1,vector<bool>(m+1));
        f[0][0]=true;
        for(int i=0;i<=n;i++){
            for(int j=0;j<=m;j++){
                if(i&&a[i]==c[i+j])f[i][j]=f[i][j]||f[i-1][j];
                if(j&&b[j]==c[i+j])f[i][j]=f[i][j]||f[i][j-1];
               // cout<<f[i][j];
            }
        }
        return f[n][m];
    }
};

097. 子序列的数目 (多想想,不太熟)

  //  typedef long long LL;
class Solution {
public:
/*
f[i][j]状态表示s 1~i 组成 t 1~j的方案数
属性cnt
状态划分
if(s[i]!=t[j]) i不能与j匹配 所以f[i][j]=f[i-1][j]
i既可以与j匹配 也可以不与j匹配f[i][j]=f[i-1][j-1]+f[i-1][j]
f[1~n][0]=1;
*/
    int numDistinct(string s, string t) {
        int n=s.size(),m=t.size();
        s=' '+s;t=' '+t;
        vector<vector<unsigned long long>>f(n+1,vector<unsigned long long >(m+1,0));
         for(int i=0;i<=n;i++)f[i][0]=1;//因为s可以从不同下标匹配
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){ 
                f[i][j]=f[i-1][j];
                if(s[i]==t[j])
                   f[i][j]+=f[i-1][j-1];
            }
        }
        return f[n][m];

    }
};

098. 路径的数目

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

099. 最小路径之和

class Solution {
public:
/*
到i,j这个点 最小代价

f[i][j] 
i>2f[i-1][j]
f[i][j-1] 
*/
    int minPathSum(vector<vector<int>>& grid) {
        int n=grid.size(),m=grid[0].size();
        vector<vector<int>>f(n+1,vector<int>(m+1,1e9));
       f[1][1]=grid[0][0];
        //要初始化正无穷
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(j>1)f[i][j]=min(f[i][j],f[i][j-1]);
                if(i>1)f[i][j]=min(f[i][j],+f[i-1][j]);
                if(i>1||j>1)f[i][j]+=grid[i-1][j-1];  
            }
        }
        return f[n][m];
    }
};

100. 三角形中最小路径之和

class Solution {
public:
/*
1
12
123
1234只能从上方或者斜上方转移
*/
    int minimumTotal(vector<vector<int>>& triangle) {
        int n=triangle.size(),m=triangle[n-1].size();
        vector<vector<int>>f(n+1,vector<int>(m+1,1e9));

        //初始化正无穷
        f[1][1]=triangle[0][0];

        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                if(i>1||j>1)f[i][j]=f[i-1][j];//除了1,1都能从上方转移

                //从斜上方转移的条件是j>=2
                if(j>1)f[i][j]=min(f[i][j],f[i-1][j-1]);
                if(i>1||j>1)f[i][j]+=triangle[i-1][j-1];
            }
        }
        int res=2e9;
        for(int i=1;i<=m;i++)res=min(res,f[n][i]);
        return res;

    }
};

101. 分割等和子集

class Solution {
public:
/*
01背包
f[i][j] 前i个选 能不能拼成j这个数
属性 bool
状态划分
选与不选
f[i-1][j] 
f[i-1][j-x] j>=x x为nums元素
*/
    bool canPartition(vector<int>& nums) {
        int m=0;
        for(auto x:nums)m+=x;
        if(m%2)return false;
        int n=nums.size();
        vector<vector<int>>f(n+1,vector<int>(m+1));
        m/=2;
        //cout<<m<<endl;
       f[0][0]=1;
        for(int i=1;i<=n;i++){
           //  f[i][0]=1;
            for(int j=0;j<=m;j++){
               
                f[i][j]=f[i-1][j];//不选i
                 int x=nums[i-1];
                if(j>=x)f[i][j]|=f[i-1][j-x];//选i
                //cout<<i<<"."<<j<<" "<<f[i][j]<<endl;
            }
        }
        return f[n][m];

    }
 bool canPartition(vector<int>& nums) {
        int m=0;
        for(auto x:nums)m+=x;
        if(m%2)return false;
        m/=2;
         vector<int>f(m+1);
       f[0]=1;
        for(int x:nums){
            for(int j=m;j>=x;j--)f[j]|=f[j-x];  
        }
        return f[m];
    }
};

102. 加减的目标值

class Solution {
public:
/*
状态表示
f[i][j] 前i个全选 总和为j的方案数
为什么全选?
不全选没意义啊 你知道前i个选其中几个 总和为j有什么用?题目用不了

属性cnt

状态划分 
选前i-1个  总和为j-x j+x的方案数
f[i-1][j-x]

f[i-1][j+x]

注意0 <= sum(nums[i]) <= 1000
审题很重要 我本以为要offset 2e5

-1000~1000 -> 0~2000
*/
    int findTargetSumWays(vector<int>& nums, int target) {
        if(target<-1000||target>1000)return 0;
        int offset=1000,n=nums.size();
        vector<vector<int>>f(n+1,vector<int>(2001));
        f[0][offset]=1;
        for(int i=1;i<=n;i++){
            int x=nums[i-1];
            for(int j=-1000;j<=1000;j++){
                if(j-x>=-1000)f[i][j+offset]+=f[i-1][j-x+offset];
                if(j+x<=1000)f[i][j+offset]+=f[i-1][j+x+offset];
            }
        }
        return f[n][target+offset];
    }
};

103. 最少的硬币数目

class Solution {
public:
/*
表示
f[j] 前i个货币选 总金额为j最少需要多少个硬币

属性min
划分
 f[i-1][j]
 f[i-1][j-x]+1

 完全背包问题 正向枚举

 0块钱只需要0个硬币 f[0]=0
*/
    int coinChange(vector<int>& coins, int m) {
        vector<int>f(m+1,1e9);
        f[0]=0;
        for(int x:coins){
            for(int j=x;j<=m;j++){
                     f[j]=min(f[j],f[j-x]+1);
            }
        }
        return f[m]==1e9?-1:f[m];

    }
};

104. 排列的数目 (没想明白)

class Solution {
public:
/*
这题不是完全背包会将 所有排列方案当成一种

为什么先枚举j没想明白
*/
    int combinationSum4(vector<int>& nums, int m) {
        vector<long long >f(m+1,0);
        f[0]=1;
     for(int j=1;j<=m;j++)
     for(int x:nums){
         if(j>=x)f[j]=(f[j]+f[j-x])%INT_MAX;
     }
        return f[m];
    }
};
posted @ 2022-03-25 20:22  liv_vil  阅读(25)  评论(0)    收藏  举报