LeetCode(4)——动态规划

  • 动态规划指把问题分解为多个阶段,每个阶段对应一个决策。我们记录每一个阶段可达的状态集合(去掉重复的),然后通过当前阶段的状态集合,来推导下一个阶段的状态集合,动态地往前推进。这也是动态规划这个名字的由来。
//背包问题C++代码动态规划
class Solution { 
public:
//weight:物品重量,n:物品个数,w:背包可承载重量
int knapsack(vector<int>& weight, int n, int w) {
  vector<vector<bool> > states(n,vector<bool>(w+1)) ; // 默认值false
  states[0][0] = true;  // 第一行的数据要特殊处理,可以利用哨兵优化
  if (weight[0] <= w) {
    states[0][weight[0]] = true;
  }
  for (int i = 1; i < n; ++i) { // 动态规划状态转移
    for (int j = 0; j <= w; ++j) {// 不把第i个物品放入背包
      if (states[i-1][j] == true) states[i][j] = states[i-1][j];
    }
    for (int j = 0; j <= w-weight[i]; ++j) {//把第i个物品放入背包
      if (states[i-1][j]==true) states[i][j+weight[i]] = true;
    }
  }
  for (int i = w; i >= 0; --i) { // 输出结果
    if (states[n-1][i] == true) return i;
  }
  return 0;
}
};

1. word-break(leetcode 139)
class Solution {
public://递推,f(i) = f(j) && f(s(i-j))
    bool wordBreak(string s, unordered_set<string> &dict) {
        vector<bool> res(s.size()+1);
        res[0] = true;
        for(int i=1;i<res.size();++i){
            for(int j=0;j<i;++j){
                if(res[j] && dict.find(s.substr(j,i-j))!=dict.end()){
                    res[i] = true;
                    break;
                }
            }
        }
        return res[s.size()];
    }
};
2. candy(leetcode 135)

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。

那么这样下来,老师至少需要准备多少颗糖果呢?
双数组法

class Solution {
public:
    int candy(vector<int>& ratings) {
        vector<int> right(ratings.size(),1);
        vector<int> left(ratings.size(),1);
        for(int i=1;i<ratings.size();++i){
            if(ratings[i]>ratings[i-1])
                right[i] = right[i-1]+1;
        }
        for(int i=ratings.size()-2;i>=0;--i){
            if(ratings[i]>ratings[i+1])
                left[i] = left[i+1]+1;
        }
        int sum = 0;
        for(int i=0;i<ratings.size();++i){
            sum += max(left[i],right[i]);
        }
        return sum;
    }
};
3.分割回文串i(leetcode 131)

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。

class Solution {
    vector<vector<string>> res;
public:
    bool Ispalind(string s){
        return s==string(s.rbegin(),s.rend());
    }
    void dfs(string s,vector<string>& cur){
        if(s==""){
            res.push_back(cur);
            return;
        }
        //每一次都判断前i个字符是否可以组成回文串,size()长的字串,则有size()个阶段
        for(int i=1;i<=s.size();++i){//分为size个路径进行dfs搜索
            string tmp = s.substr(0,i);//取从第0个开始的i个字符组成新的串
            if(Ispalind(tmp)){//对于满足条件的就判断其余的字符串部分
                cur.push_back(tmp);
                dfs(s.substr(i,s.size()-i),cur);
                cur.pop_back();
            }
        }
    }
    vector<vector<string>> partition(string s) {
        vector<string> cur;
        dfs(s,cur);
        return res;
    }
};

牛客刷题后有大佬说:“如果要求输出所有可能的解,往往都是要用深度优先搜索。如果是要求找出最优的解,或者解的数量,往往可以使用动态规划。”

4.分割回文串ii(leetcode 132)

给出一个字符串s,分割s使得分割出的每一个子串都是回文串
计算将字符串s分割成回文分割结果的最小切割数

class Solution {
public:
    //dp[i] = min(d[j]+1,dp[i]);--->j rangein[0,i)
    int minCut(string s) {
        if(s.empty())
            return 0;
        int len = s.size();
        vector<vector<bool> > checkpalind(len,vector<bool>(len,false));//查出所有满足条件的回文串
        //初始条件是所有单个字符必为回文,以及相邻的两个字符若相等则为回文串
        for(int i=0;i<len;++i){//分阶段,i表示子串的结束位置
            for(int j=0;j<=i;++j){//分阶段,j表示子串的开始位置
                if(s[i]==s[j]&&(i-j<=2 || checkpalind[j+1][i-1]))//动态规划
                //j-->i的子串若是回文串应该满足(j+1)-->(i-1)子串也为回文串的要求
                    checkpalind[j][i] = true;
            }
        }
        //dp求最少分割次数,每一位表示从头至此的最少分割次数
        vector<int> res(s.size());
        for(int i=0;i<s.size();++i)//初始假设无回文串,只能一个个分割
            res[i] = i;
        for(int i=1;i<s.size();++i){//动态规划开始,n-1个阶段
            if(checkpalind[0][i]){//若自身就是一个回文串则不用切割
                res[i] = 0;//置为0
                continue;
            }
            for(int j=0;j<i;++j){//dp[i] = min(d[j]+1,dp[i]);--->j rangein[0,i)
                if(checkpalind[j+1][i]){
                    res[i] = min(res[j]+1,res[i]);
                }
            }
        }
        return res[s.size()-1];//最小分割次数
    }
};
5.最长回文子串(leetcode 5)

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

class Solution {
public:
    string longestPalindrome(string s) {
        //dp[i][j] = s[i]==s[j] && dp[i+1][j-1],满足条件则为回文串
        if(s.size()<=1)
            return s;
        int len = s.size();
        vector<vector<bool> > dp(len+1,vector<bool>(len+1,false));
        for(int i=1;i<=len;++i) dp[i][i]=true;
        int pos=0,maxsize=1;
        for(int j=2;j<=len;++j){
            for(int i=1;i<j;++i){
                if(s[i-1]==s[j-1] && (j-i==1 || dp[i+1][j-1])){
                    dp[i][j] = true;
                    if(j-i+1>maxsize){
                        pos = i-1;
                        maxsize = j-i+1;
                    }
                }
            }
        }
        return s.substr(pos,maxlen);
    }
};

总之一定要抓住递推公式,在此基础上分阶段进行求解,不能放过其他任何情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值