文章目录
入门动态规划:
记忆化模型搜索,优化重复计算节点(需要hash表进行存储状态),还有注意边界值,防止一直递归下去.
可以倒着计算,从n-1计算,拿出每次最好的方案进行计算
70. 爬楼梯
class Solution {
int dp[50];
public:
int climbStairs(int n) {
dp[0] = dp[1] =1;
for(int i=2;i<=n;i++)
{
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
};
剑指 Offer 10- II. 青蛙跳台阶问题
class Solution {
#define mod 1000000007
int f[107]; //表示跳上i级台阶的方案数
public:
int numWays(int n) {
f[0] = f[1] = 1;
for(int i=2;i<=n;i++)
{
f[i] = (f[i-1] + f[i-2])%mod; //递推思想 因为到达第n阶只有两种方法,一种是从n-1上一步,第二种是从n-2上走两步,故需要保存两种方法的方案即可
}
return f[n];
}
};
746. 使用最小花费爬楼梯
class Solution {
#define len 1001
int f[len];
public:
int minCostClimbingStairs(vector<int>& cost) {
f[0] = f[1] = 0;
int n = cost.size();
for(int i=2;i<=n;i++)
{
f[i] = min(cost[i-1]+f[i-1],cost[i-2]+f[i-2]); //也是递归思想,存储了之前的方案
}
return f[n];
}
};
198. 打家劫舍
class Solution { //注意当n=1时直接返回dp[0]即可,但是需进行一个dp[1]最大的判断(确定边界),然后找出状态转移方程
#define maxn 110
int dp[maxn];
public:
int rob(vector<int>& nums) {
dp[0] = nums[0];
int n = nums.size();
for(int i=1;i<n;i++)
{
if(i==1)
dp[1] = max(nums[0],nums[1]);
else {
dp[i] = max(dp[i-1],dp[i-2] + nums[i]);//这会进行一次判断,当i-2时加上本身和i-1时判断(判断触发是否少拿一个临近的大,还是多拿一个大)
}
}
return dp[n-1];
}
};
213. 打家劫舍 II
class Solution {
#define maxn 110 //这题的边界为第一个选还是不选
int dp[maxn][2]; //dp[i][0]表示在到第i个元素为止,第一个元素不选,dp[i][1]表示到第i-1个元素为止,第一个元素选取
public:
int rob(vector<int>& nums) {
//因为首尾相连,讨论选0还是选1这个问题
int n = nums.size();
if(n==1)
return nums[0];
else if(n==2)
return max(nums[0],nums[1]);//判断临界条件n=0|1时
dp[0][1] = nums[0];
dp[0][0] = 0;
for(int i=0;i<n;i++)
{
for(int j=0;j<2;j++)
{
if(i==1){
if(j==0)
{
dp[i][j] = nums[1];
}
else dp[i][j] = nums[0];
}
else if(i==n-1&&j==1)
{
dp[i][j] = dp[i-1][j];
}
else {
dp[i][j] = max(dp[i-1][j],dp[i-2][j]+nums[i]);
}
}
}
return max(dp[n-1][0],dp[n-1][1]);
}
};//这题一直过不了编译,不理解,但是算法是对的
91. 解码方法
class Solution {
#define maxn 115
int dp[maxn];
public:
int numDecodings(string s) {
int len=s.size();
int n=len;
for(int i=0;i<len;i++)
{
dp[i] = 0;
if(i==0){
dp[i] = (s[i]=='0')?0:1;
}
else {
if(s[i]!='0')
{
dp[i] += dp[i-1];
}
if(i>=1&&(s[i-1]=='1'||s[i-1]=='2'))//i==1这个临界注意写上
{
int val =(s[i-1]-'0')*10 + s[i]-'0';
if(val<=26){
if(i==1){
dp[i]++;
}
else {
dp[i] += dp[i-2];//这一步加dp[i-2]相当于把i-1和第i位合并成一个数字映射,而前面的加上dp[i-1]是相当于把它看成数字单独映射(这一步是把两种情况进行了合并,关键一步,比较难懂)
}
}
}
}
}
return dp[n-1];
}
};
1646. 获取生成数组中的最大值
class Solution {
#define maxn 110
int nums[maxn];
public:
int getMaximumGenerated(int n) {
nums[0] = 0;
nums[1] = 1;
for(int i=0;i<n;i++)
{
if(2<=2*i && 2*i<=n)
{
nums[2*i] = nums[i];
}
if(2<=2*i+1 && 2*i+1<=n)
{
nums[2*i+1] = nums[i] +nums[i+1];
}
}
int v = nums[0];
for(int i=1;i<=n;i++)
{
v = max(v ,nums[i]);
}
return v;
}
};//这题直接告诉了状态转移方程,我们求最大值
1043. 分隔数组以得到最大和
class Solution { //这个没看懂
#define maxn 1010
int dp[maxn];
public:
int maxSumAfterPartitioning(vector<int>& arr, int k) {
int maxv,cnt;
int n = arr.size();
for(int i=0;i<n;i++)
{
maxv = 0;
cnt = 0;
dp[i] = 0;
for(int j=i;j>=0;j--)
{
if(arr[j]>maxv)
maxv = arr[j]; //找到最大值
cnt++;
if(cnt>k) break;
if(j){
dp[i] = max(dp[i],dp[j-1] + maxv * cnt);//
}
else {
dp[i] = max(dp[i],cnt*maxv);//判断小于0的情况
}
}
}
return dp[n-1];
}
};
139. 单词拆分
这题运用了后缀检验,动态规划思想
其中dp[i]为状态,如果我们能通过wordDict拼接出前i个字符串,则dp[i]为0,否则为0
解题思路
这题运用了后缀检验,动态规划思想
其中dp[i]为状态,如果我们能通过wordDict拼接出前i个字符串,则dp[i]为0,否则为0
然后我们需要遍历枚举dp[i],去和每一个字符串去进行后缀匹配
字典太长跳过匹配,前i-1字符串个为不能匹配跳过匹配,找不到合适的后缀进行匹配则跳出循环
class Solution { //其中第二个if里面的判断一点要注意是判断i-l即为上一个后缀匹配
#define maxn 510
int dp[maxn];
public://按位匹配
bool wordBreak(string s, vector<string>& wordDict) {//字典太长跳过匹配,前i-1字符串个为不能匹配跳过匹配,找不到合适的后缀进行匹配则跳出循环
int n = s.size();
int i,j,l,k;
memset(dp,0,sizeof(dp));
for(i = 0;i<n;++i)
{
for(j=0;j<wordDict.size();++j)
{
l = wordDict[j].size();
if(i-l+1<0)//长度是否匹配
{
continue;
}
if(i-l!=-1&&!dp[i-l])//!dp[i-l]是为了验证上一个后缀是否匹配,如果不匹配,则直接跳过,i-l!=-1是为了看是否为第一个匹配如果为第一个匹配则不需要判断后缀匹配
{
continue;
}
for(k=0;k<l;k++)
{
if(s[i-l+1+k]!=wordDict[j][k])
break;
}
if(k==l)
{
dp[i]=1;
break;
}
}
}
return dp[n-1];
}
};
1869. 哪种连续子字符串更长
class Solution {
#define maxn 110
int dp[2][maxn];//dp[0][i]表示以第i个数结尾且连续'0'的个数,dp[1][i]表示以第i个数结尾连续'1'的数
int maxv[2];
public:
bool checkZeroOnes(string s) {
int n = s.size();
memset(dp,0,sizeof(dp));
maxv[0] = maxv[1] =0;
dp[s[0]-'0'][0] = 1;
maxv[s[0]-'0'] = 1;
for(int i=1;i<n;++i)
{
if(s[i]==s[i-1])
{
dp[s[i]-'0'][i] = dp[s[i]-'0'][i-1] + 1;
} else {
dp[s[i]-'0'][i] = 1;
}
maxv[0] = max(maxv[0],dp[0][i]);
maxv[1] = max(maxv[1],dp[1][i]);
}
return (maxv[1] > maxv[0]);
}
};
724. 寻找数组的中心下标
class Solution {
#define maxn 10100
int sum[maxn];
public:
int pivotIndex(vector<int>& nums) {
//状态转换方程 感觉这题的转换方程并不是分麻烦,前缀和比较多
int n = nums.size();
memset(sum,0,n);
sum[0] = nums[0];
for(int i=1;i<n;i++)
{
sum[i] = sum[i-1] + nums[i];
}
if(sum[n-1] == sum[0])
return 0;
for(int i=1;i<n;i++)
{
if(sum[i-1] == sum[n-1] - sum[i])
return i;
}
return -1;
}
};
1277. 统计全为 1 的正方形子矩阵
class Solution {
#define maxn 310
bool mat[maxn][maxn][maxn];//开三维是为了
public:
int countSquares(vector<vector<int>>& matrix) {
int l,j,i;
int n = matrix.size();
int m = matrix[0].size();
int len = min(m,n);
int ans = 0;
for(l=1;l <=len;l++)//l是控制搜索的宽度
{
for(i=0;i+l<=n;i++)
{
for(j=0;j+l<=m;j++)
{
if(l==1)
mat[l][i][j] = matrix[i][j];
else
mat[l][i][j] = matrix[i][j]&&mat[l-1][i+1][j]&&mat[l-1][i][j+1]&&mat[l-1][i+1][j+1];
ans +=mat[l][i][j];
}
}
}
return ans;
}
};