您的位置:首页 > 财经 > 产业 > 网络营销方式有些什么_郑州妇科医院正规有哪些_如何引流被动加好友微信_青岛百度快速优化排名

网络营销方式有些什么_郑州妇科医院正规有哪些_如何引流被动加好友微信_青岛百度快速优化排名

2024/12/22 18:35:54 来源:https://blog.csdn.net/MXZSL/article/details/143893037  浏览:    关键词:网络营销方式有些什么_郑州妇科医院正规有哪些_如何引流被动加好友微信_青岛百度快速优化排名
网络营销方式有些什么_郑州妇科医院正规有哪些_如何引流被动加好友微信_青岛百度快速优化排名

目录

动态规划的基本流程:

状态表示

状态转移方程

初始化

填表顺序

返回值

空间优化:

斐波那契数列模型问题

1、第N个泰波那契数

2、三步问题

3、使用最小花费爬楼梯

4.解码方法

路径问题

 1、不同路径1

2、不同路径2

3.礼物的最大价值

4.下降路径最小和

5.最小路径和

6.地下城游戏(难度upup)


动态规划的基本流程:

状态表示

        动态规划基本都是要定义一个数组,一维或二维,我们通过某种方式将数组填满,数组的某一个值就是我们需要的答案。比如dp数组

而数组的任意一个值所代表的含义,就是状态表示 

怎么看出状态表示?

1、题目要求中,会有关键信息,很有可能就是正确的状态表示

2、经验+题目要求,多刷题结合题目要求。

3、要学会分析问题,从问题中找到子问题。

这三个层次基本就是对动态规划的不同熟练程度了。

状态转移方程

上面说了,我们要将数组填满,那怎么填呢,也就是令任意的dp[i]等于什么值呢?

状态转移,从字面意义上,我们可以理解为将状态进行了转移,对应到dp数组,就是dp[i]等于dp的任意一个或多个不同的数组值在经历了一定的变换后的结果,简单的就是比如全部累加,或者平均值等等。

初始化

上面的状态转移中,我们知道dp[i]是1个或多个值经历一定变化后的结果。我们感性的想想,数组最初开辟出来都是0,那不管怎么操作,大概率每个位置还是0吧,那这时候,我们就必须将数组的一些位置手动填上数字,一方面让整个流程跑起来,另一方面,就是以免中途遇到一些没有初始化的值,导致最后的结果变得莫名其妙了。

填表顺序

填当前dp[i]的时候,我们要保证用到的其他状态,已经是填过的或者已经初始化过的,这样才能让结果正确

返回值

题目要求+状态表示。

通过这两者,将dp数组中代表答案的输出即可,比如dp[n]

空间优化:

主要是节省空间的使用。

斐波那契数列模型问题

1、第N个泰波那契数

1137. 第 N 个泰波那契数 - 力扣(LeetCode)

 状态表示:

根据题目要求,dp[i]代表第i个泰波那契数

状态转移方程:

dp[i]=dp[i-3]+dp[i-2]+dp[i-1]

初始化:

dp[0]=0,dp[1]=dp[2]=1

填表顺序:

从下标3开始

返回值:

dp[n],第n个泰波那契数

class Solution {
public:int tribonacci(int n) {vector<int>mp(n+1,0);if(n==0)return 0;if(n==1||n==2)return 1;mp[0]=0,mp[1]=1,mp[2]=1;for(int i=3;i<=n;i++){mp[i]=mp[i-3]+mp[i-2]+mp[i-1];}return mp[n];}
};

下面是节省空间的写法。

用滚动数组,只用开4位。

class Solution {
public:int tribonacci(int n) {int mp[4];if(n==0)return 0;if(n==1||n==2)return 1;mp[0]=0,mp[1]=1,mp[2]=1;for(int i=3;i<=n;i++){int x=3;mp[x]=mp[x-3]+mp[x-2]+mp[x-1];mp[0]=mp[1];mp[1]=mp[2];mp[2]=mp[3];}return mp[3];}
};

2、三步问题

面试题 08.01. 三步问题 - 力扣(LeetCode)

 状态表示:

根据题目要求,dp[i]代表台阶[i]一共有多少种方法跳上来

状态转移方程:

dp[i]=dp[i-3]+dp[i-2]+dp[i-1]

初始化:

dp[1]=1,dp[2]=2,dp[3]=4

填表顺序:

从下标4开始

返回值:

dp[n],第n个台阶的方法总数

class Solution {
public:int waysToStep(int n) {vector<int>mp(n+4,0);mp[1]=1;mp[2]=2;mp[3]=4;for(int i=4;i<=n;i++){if(i-3>=1)mp[i]+=mp[i-3];mp[i]%=1000000007;if(i-2>=1)mp[i]+=mp[i-2];mp[i]%=1000000007;if(i-1>=1)mp[i]+=mp[i-1];mp[i]%=1000000007;}return mp[n];}
};

滚动数组优化

class Solution {
public:int waysToStep(int n) {vector<int>mp(5,0);mp[1]=1;mp[2]=2;mp[3]=4;if(n==1)return 1;if(n==2)return 2;if(n==3)return 4;const int mod=1000000007;for(int i=4;i<=n;i++){mp[4]=((mp[1]+mp[2])%mod+mp[3])%mod;mp[1]=mp[2];mp[2]=mp[3];mp[3]=mp[4];}return mp[4];}
};

3、使用最小花费爬楼梯

746. 使用最小花费爬楼梯 - 力扣(LeetCode)

思路1:

 状态表示:

根据题目要求,dp[i]代表跳到台阶i的最小花费

状态转移方程:

dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])

初始化:

dp[0]=dp[1]=0

填表顺序:

从下标2开始

返回值:

dp[n],跳到楼顶的最小花费

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int n=cost.size();vector<int>dp(n+1,0);dp[0]=0;dp[1]=0;for(int i=2;i<=n;i++){dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);}return dp[n];}
};

思路2:

 状态表示:

根据题目要求,dp[i]代表从[i]台阶到楼顶的最小花费

状态转移方程:

dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i])

初始化:

dp[n]=0,dp[n-1]=cost[n-1];

填表顺序:

从下标n-2开始,从右往左

返回值:

min(dp[0],dp[1])跳到楼顶的最小花费

class Solution {
public:int minCostClimbingStairs(vector<int>& cost) {int n=cost.size();vector<int>dp(n+1,0);dp[n-1]=cost[n-1];dp[n]=0;for(int i=n-2;i>=0;i--){dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i]);}return  min(dp[0],dp[1]);}
};

4.解码方法

状态表示:

根据题目要求,dp[i]代表以i为结尾的方法总数

状态转移方程:

if(s[i]!='0')dp[i]=dp[i-1];//如果当前字符不是0的话,说明可以在前面i-1个字符解码方法的基础上再加个当前字符即可,所以直接赋值

 if(s[i-1]!='0'&&(s[i-1]-'0')*10+s[i]-'0'<=26)//如果前一个不是0(有前导0,不能合并解码),并且下标i-1字符和i字符组成的数小于等于26,说明可以合并解码,让dp[i]+=dp[i-2],也就是在前i-2个字符的基础上加上这个合并解码,所以加的方法数就是dp[i-2]

 {

                if(i-2>=0)//越界了,那说明是前2个字符触发这个了,那就是再加1

                dp[i]+=dp[i-2];

                else dp[i]+=1;

 }

初始化:

dp[0]=1

填表顺序:

从下标1开始,从左往右

返回值:

dp[s.size()-1],以最后一个字符为结尾的解码方法总数

class Solution {
public:int numDecodings(string s) {if(s[0]=='0')return 0;//如果第一个就是0,那直接返回0即可dp[0]=1;//第一个肯定不是0,那肯定可以单独解码,也就是1for(int i=1;i<s.size();i++){if(s[i]!='0')dp[i]=dp[i-1];if(s[i-1]!='0'&&(s[i-1]-'0')*10+s[i]-'0'<=26){if(i-2>=0)dp[i]+=dp[i-2];else dp[i]+=1;}}return dp[s.size()-1];}int dp[101];
};

其实,也可以让下标映射整体往后挪一位,然后让下标0存1,这样就不用特意判断i-2>=0了

路径问题

 1、不同路径1

62. 不同路径 - 力扣(LeetCode)

这个在我记忆化搜索里其实写过。

状态表示:

根据题目要求,dp[i][j]代表走到[i][j]位置的不同路径和

状态转移方程:

dp[i][j]=dp[i-1][j]+dp[i][j-1]

初始化:

dp[1][1]=1,并且i、j其中任意一个为0的时候,都是0,这是一种虚拟节点,保证状态转移方程在‘越界’的时候结果依旧正确

填表顺序:

从[1][1]位置开始一行行遍历,每行从左往右

返回值:

dp[m][n],即到[m][n]位置的不同路径和

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

2、不同路径2

63. 不同路径 II - 力扣(LeetCode)

比上一题多了障碍物的概念

状态表示:

根据题目要求,dp[i][j]代表走到[i][j]位置的不同路径和

状态转移方程:

dp[i][j]=dp[i-1][j]+dp[i][j-1]

初始化:

dp[0][0]=obstacleGrid[0][0]==1?0:1;

填表顺序:

从[0][0]位置开始一行行遍历,每行从左往右

返回值:

dp[m][n],即到[m][n]位置的不同路径和

class Solution {
public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {dp[0][0]=obstacleGrid[0][0]==1?0:1;int m=obstacleGrid.size();int n=obstacleGrid[0].size();for(int i=0;i<m;i++){for(int j=0;j<n;j++){if(j==0&&i==0)continue;if(obstacleGrid[i][j]==1)continue;if(i-1>=0)dp[i][j]+=dp[i-1][j];if(j-1>=0)dp[i][j]+=dp[i][j-1];}}return dp[m-1][n-1];}int dp[101][101];
};

因为从[0][0]开始走,所以要注意下标越界问题,另外如果起始位置有障碍物,方法是0的,其实这时候可以直接返回0的,但我为了整体代码美观,就没写。

在遍历过程中,如果当前位置有障碍物,那就是0的,直接continue掉即可。

这个代码还可以把下标映射整体往后挪一位,这样不用写这些边界if判断

class Solution {
public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int m=obstacleGrid.size();int n=obstacleGrid[0].size();dp[1][0]=1;for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)if(obstacleGrid[i-1][j-1]==0)dp[i][j]=dp[i-1][j]+dp[i][j-1];return dp[m][n];}int dp[101][101];
};

这个看起来更加简洁

3.礼物的最大价值

礼物的最大价值_牛客题霸_牛客网 (nowcoder.com)

这题前面两题的最大区别就是,本题的每个位置都有一个价值

状态表示:

根据题目要求,mp[i][j]代表移动到[i][j]位置的最大礼物价值

状态转移方程:

mp[i][j]=grid[i-1][j-1]+max(mp[i-1][j],mp[i][j-1]);

初始化:

无需特意初始化,只要把二维数组多开一圈,依靠默认的0即可,即凡是下标含0的,值都为0

填表顺序:

从[1][1]开始遍历,逐行变量,每行都从左往右,保证[i-1][j]和[i][j-1]的位置的值比当前位置早处理即可

返回值:

mp[n][m],即到[n][m]位置的最大礼物价值

class Solution {
public:/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param grid int整型vector<vector<>> * @return int整型*/int maxValue(vector<vector<int> >& grid) {int n=grid.size(),m=grid[0].size();vector<vector<int>>mp(n+1,vector<int>(m+1));for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){mp[i][j]=grid[i-1][j-1]+max(mp[i-1][j],mp[i][j-1]);}}return mp[n][m];}
};

4.下降路径最小和

931. 下降路径最小和 - 力扣(LeetCode)

本质跟前面几题都是一样的,只是路径的选择稍稍有变化

状态表示:

根据题目要求,mp[i][j]代表移动到[i][j]位置的最小下降路径和

状态转移方程:

mp[i][j]=matrix[i-1][j-1]+min(min(mp[i-1][j],mp[i-1][j-1]),mp[i-1][j+1]);

初始化:

数组先开大一点,比如n+3或者n+2也行,主要是防止越界

数组初始化设为一个很大的值(因为是最小值,所以如果访问到了边界,这些值默认都应该为很大,这样才不会影响正常的路径选择)

[0][1~n+1]都要初始化为0,如果不初始化为0,[0][1~n+1]会被上面所设的很大的值影响,导致结果会被放大很多。

填表顺序:

从[1][1]开始遍历,逐行变量,每行都从左往右,保证[i-1][j]和[i-1][j-1]、[i-1][j+1]的位置的值比当前位置早处理即可

返回值:

当i遍历到最后一行,提前设置变量,对最后一行的每一个结果,进行比较,返回最小值即可

class Solution {
public:int minFallingPathSum(vector<vector<int>>& matrix) {int n=matrix.size();vector<vector<int>>mp(n+2,vector<int>(n+2,9999999999));for(int i=0;i<=n+1;i++){mp[0][i]=0;}int ans=9999999999;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){mp[i][j]=matrix[i-1][j-1]+min(min(mp[i-1][j],mp[i-1][j-1]),mp[i-1][j+1]);if(i==n){ans=min(ans,mp[i][j]);}}}return ans;}
};

5.最小路径和

64. 最小路径和 - 力扣(LeetCode)

思路没有太多区别,可以用动归解决的路径问题,在转移方程上基本都是大同小异,核心变化反而是对于初始化的问题

状态表示:

根据题目要求,dp[i][j]代表移动到[i][j]位置的最小下降路径和

状态转移方程:

 dp[i][j]=min(dp[i][j-1],dp[i-1][j])+grid[i-1][j-1];

初始化:

数组先开n+1

数组初始化设为一个很大的值(因为是最小值,所以如果访问到了边界,这些值默认都应该为很大,这样才不会影响正常的路径选择)

因为对于[i][j]来说,只能从[i][j-1]和[i-1][j]走过来,所以要保证边界值不影响[i][j]的判断,对于第1行来说,他们只能从左边获取值,不能从上面获取值,而由于上面默认都设置了大值,导致[1][1]的位置无法正常设置,因为对于[1][1]来说,[0][1]和[1][0]都是一个大值,选哪一个都会导致结果异常,所以[1][1]必须提前设置,并且后续不能循坏到[1][1]

填表顺序:

从[1][1]开始遍历,逐行变量,每行都从左往右,保证[i-1][j]和[i][j-1]的位置的值比当前位置早处理即可

返回值:

返回dp[m][n]即可,即到达[m][n]位置的最小路径和

class Solution {
public:int minPathSum(vector<vector<int>>& grid) {int m=grid.size();int n=grid[0].size();vector<vector<int>>dp(m+1,vector<int>(n+1,INT_MAX));dp[1][1]=grid[0][0];for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){if(i==1&&j==1)continue;dp[i][j]=min(dp[i][j-1],dp[i-1][j])+grid[i-1][j-1];}}return dp[m][n];}
};

6.地下城游戏(难度upup)

174. 地下城游戏 - 力扣(LeetCode)

虽然同样是路径问题,但是在状态表示上有很大的不同。

状态表示:

根据题目要求,dp[i][j]代表进入[i][j]前,并且一定以[i][j]为起点,移动到[m][n]位置的最小健康值

注意,这跟前面有很大的区别,前面基本都是移动到[i][j],以[i][j]结尾,而这里是以[i][j]开始。

之所以这样,是因为本题后面的值,是会影响最初的健康值的。简而言之,要以不变往变走。

状态转移方程:

dp[i][j]=min(dp[i+1][j],dp[i][j+1])-dungeon[i-1][j-1];   

假设dp[i][j]==x,dp[i-1][j+1]==y,dungeon[i-1][j-1]==mp[i-1][j-1],那么dp[i][j+1]只可能是x+mp[i-1][j-1]或y+mp[i-1][j-1],因此x>=dp[i][j+1]-mp[i-1][j-1],同理x>=dp[i+1][j]-mp[i-1][j-1];所以x==min(dp[i+1][j],dp[i][j+1])-dungeon[i-1][j-1]

dp[i][j]=max(1,dp[i][j]); 

然而考虑到一个问题,那就是mp[i-1][j-1]可能大于min(dp[i+1][j],dp[i][j+1]),也就是某个房间加了大量的健康值,有人可能疑问为什么,因为在我们这个dp的第一个关系式中,每一个位置的关系是取决于后面房间的,也就是说,前一个房间和当前房间是没有关系的,这样的话mp[i-1][j-1]就完全有可能大于min(dp[i+1][j],dp[i][j+1]),因此遇到这种情况,说明只要以最小正整数1进入[i][j]位置,就可以保证后面可以走到[m][n]

初始化:

初始开[m+2][n+2]

全部填大值,然后让dp[m][n]=dungeon[m-1][n-1]>0?1:abs(dungeon[m-1][n-1])+1;

填表顺序:

从[m][n-1]开始遍历,逐行变量,每行都从右往左,保证[i+1][j]和[i][j+1]的位置的值比当前位置早处理即可

返回值:

返回dp[1][1]即可,即进入[1][1]前,并且一定以[1][1]为起点,移动到[m][n]位置的最小健康值

class Solution {
public:int calculateMinimumHP(vector<vector<int>>& dungeon) {int m=dungeon.size();int n=dungeon[0].size();vector<vector<int >>dp(m+2,vector<int>(n+2,INT_MAX));dp[m][n]=dungeon[m-1][n-1]>0?1:abs(dungeon[m-1][n-1])+1;for(int i=m;i>=1;i--){for(int j=n;j>=1;j--){if(i==m&&j==n)continue;dp[i][j]=min(dp[i+1][j],dp[i][j+1])-dungeon[i-1][j-1];dp[i][j]=max(1,dp[i][j]);}}return dp[1][1];}
};

本题难度是有的,主要是状态表示和状态转移方程上面有一定难度。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com