- 动态规划指把问题分解为多个阶段,每个阶段对应一个决策。我们记录每一个阶段可达的状态集合(去掉重复的),然后通过当前阶段的状态集合,来推导下一个阶段的状态集合,动态地往前推进。这也是动态规划这个名字的由来。
//背包问题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);
}
};
总之一定要抓住递推公式,在此基础上分阶段进行求解,不能放过其他任何情况