week8

 

从集合的角度来考虑dp问题

寻找某一状态来代表一类东西

dp考虑方式:1.状态表示 f[i] :(1)集合:所有以i结尾的子段(2) 属性max/min/数量。

                       2.状态计算——集合的划分。

f[i]=max(0,f[i-1])+nums[i];

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int res=INT_MIN,last=0;
        for(int i=0;i<nums.size();i++)
        {
            int now=max(0,last)+nums[i];
            res=max(res,now);
            last=now;
        }
        return res;
    }
};
class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
         int n=triangle.size();
        vector<int> f(triangle[n-1].begin(),triangle[n-1].end()),g(n);
        for(int i=n-2;i>=0;i--)
        {
            for(int j=0;j<=i;j++)
                g[j]=min(f[j],f[j+1])+triangle[i][j];
            f=g;
        }
        return f[0];
    }
};
class Solution {
public:
    int minimumTotal(vector<vector<int>>& nums) {
         int n=nums.size();
         vector<vector<long long>> f(2,vector<long long>(n));
         f[0][0]=nums[0][0];
         for(int i=1;i<n;i++)
         {
             for(int j=0;j<=i;j++)
             {
                 f[i&1][j]=INT_MAX;
                 if(j>0) f[i&1][j]=min(f[i&1][j],f[i-1 & 1][j-1]+nums[i][j]);
                 if(j<i) f[i&1][j]=min(f[i&1][j],f[i-1 & 1][j]+nums[i][j]);
             }
         }
         long long res=INT_MAX;
         for(int i=0;i<n;i++)
            res=min(res,f[n-1&1][i]);
        return res;
    }
};
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& g) {
        int n=g.size(),m=g[0].size();
        vector<vector<long long>> f(n,vector<long long>(m));
        f[0][0]= !g[0][0];
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(g[i][j]) continue;
                if(i>0) f[i][j]+=f[i-1][j];
                if(j>0) f[i][j]+=f[i][j-1];
            }
        }
        return f[n-1][m-1];
    }
};

线性dp

区间dp

背包dp

f[i]表示前i个数的组合情况

s[i-1]表实前i个数中最后一个数

f[0]=1是设置的边界,这个边界非常巧妙,让你在算s[0]和s[0]s[1]时可得到f[2]=2,而且无需判断字长有没有2。这样在算所以f[1]+=f[0],(实际上在算是s[0]这个点)。可以这么认为在一个i长的字符串中,确定前i-1(或i-2)个就确定了全部.

class Solution {
public:
    int numDecodings(string s) {
        n=s.size();
        vector<int> f(n+1);
        f[0]=1;
        for(int i=1;i<n;i++)
        {
            if(s[i-1]!='0')
                f[i]+=f[i-1];

            if(i>=2)
            {
                int a=(s[i-2]-'0')*10+s[i-1]-'0';
                if(a>=10 && a<= 26)
                    f[i]+=f[i-2];
            }
        }
        return f[n];
        
    }
};

 

class Solution {
public:
    int rob(int[] nums)  {
        int n=nums.size();

        vector<int> f(n+1),g(n+1);
        for(int i=1;i<=n;i++)
        {
            f[i]=max(f[i-1],g[i-1]);
            g[i]=f[i-1]+nums[i-1];
        }

        return max(f[n],g[n]);
    }
};

 

300. 最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4

说明:

  • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
  • 你算法的时间复杂度应该为 O(n2) 。

集合:所有以i结尾的上升子序列

属性:长度的最大值

状态计算:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        vector<int> f(n);//

        for(int i=0;i<n;i++)
        {
            f[i]=1;
            for(int j=0;j<i;j++)
                if(nums[i]>nums[j])
                    f[i]=max(f[i],f[j]+1);
        }

        int res=0;
        for(int i=0;i<n;i++)
         res=max(f[i],res);
        return res;
    }
};

 

72. 编辑距离

给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  1. 插入一个字符
  2. 删除一个字符
  3. 替换一个字符

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

集合:所有将第一个字符串的前i个字母,变成第二个字符串的前j个字母的方案。

属性:min

状态计算:(考虑最后一步)

insert:f[i,j-1] +1;

delete: f[i-1,j]+1;

replace:(1)f[i-1,j-1]+1;(2)f[i-1,j-1]

class Solution {
public:

    vector<vector<int>> f;
    int minDistance(string word1, string word2) {
        int n=word1.size(),m=word2.size();
        f=vector<vector<int>>(n+1,vector<int>(m+1));
        for(int i=0;i<=n;i++) f[i][0]=i;
        for(int i=0;i<=m;i++) f[0][i]=i;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                f[i][j]=min(f[i-1][j],f[i][j-1])+1;
                f[i][j]=min(f[i][j],f[i-1][j-1]+(word1[i-1]!=word2[j-1]));
            }
        }
        return f[n][m];
    }
};

518. 零钱兑换 II

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。 

 

示例 1:

输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

示例 2:

输入: amount = 3, coins = [2]
输出: 0
解释: 只用面额2的硬币不能凑成总金额3。

示例 3:

输入: amount = 10, coins = [10] 
输出: 1

 

注意:

你可以假设:

  • 0 <= amount (总金额) <= 5000
  • 1 <= coin (硬币面额) <= 5000
  • 硬币种类不超过 500 种
  • 结果符合 32 位符号整数

 

集合:所有由前i种硬币凑出的总钱数等于j的凑法;

属性:数量

状态计算

f[i,j]=f[i-1,j]+f[i-1,j-c]+f[i-1,j-2c]+f[i-1,j-3c]+f[i-1,j-4c]+f[i-1,j-5c]+f[i-1,j-kc];

f[i,j-c]=f[i-1,j-c]+f[i-1,j-2c]+f[i-1,j-3c]+f[i-1,j-4c]+f[i-1,j-5c]+f[i-1,j-kc];

所以有:f[i,j]=f[i-1,j]+f[i,j-c];

变成一维数组:f[j]=f[j]+f[j-c];

class Solution {
public:
    int change(int m, vector<int>& coins) {
        int n=coins.size();
        vector<int> f(m+1);
        f[0]=1;
        for(auto c:coins)
            for(int j=c;j<=m;j++)
                f[j]+=f[j-c];
        return f[m];
    }
};

664. 奇怪的打印机

有台奇怪的打印机有以下两个特殊要求:

  1. 打印机每次只能打印同一个字符序列。
  2. 每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符。

给定一个只包含小写英文字母的字符串,你的任务是计算这个打印机打印它需要的最少次数。

示例 1:

输入: "aaabbb"
输出: 2
解释: 首先打印 "aaa" 然后打印 "bbb"。

示例 2:

输入: "aba"
输出: 2
解释: 首先打印 "aaa" 然后在第二个位置打印 "b" 覆盖掉原来的字符 'a'。

提示: 输入字符串的长度不会超过 100。

class Solution {
public:
    int strangePrinter(string s) {
        if(s.empty()) return 0;
        int n=s.size();
        vector<vector<int>> f(n+1,vector<int>(n+1));
        for(int len=1;len<=n;len++)
        {
            for(int l=0;l+len-1<n;l++)
            {
                int r=l+len-1;
                f[l][r]=f[l+1][r]+1;
                for(int k=l+1;k<=r;k++)
                {
                    if(s[k]==s[l])
                        f[l][r]=min(f[l][r],f[l][k-1]+f[k+1][r]);
                }
            }
        }
        return f[0][n-1];
    }
};

 

 

f[i,j]表示s的前i个字母和p的前j个字母是否匹配。

1.p[j]!=‘*’,f[i,j]=f[i-1,j-1] && (s[i]和p[j]匹配)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值