今天,我要分享的是最近参加的蓝桥杯省赛模拟赛的第四题的dp问题的解析,从零基础到基本掌握dp思想,有一定基础的同学可以直接跳到最后看答案的代码和解题思路就行。
🚀1.dp思想
相信大家都听说过状态转移方程,但是很多朋友一直以来很难入门dp的问题就是,状态转移方程是怎么来的?为什么叫状态转移方程?为什么dp问题需要状态转移方程?
🚀2.状态转移方程
状态转移方程的本质:就是通过记录前面的结果的状态,再用前面结果的状态去推出后面结果的状态。
可能大家听到这里好像还是有点迷迷糊糊,没关系,我们来看一道dp的入门题,你就明白了。
🚀3.单状态转移方程
解释是一下这里指的一个状态仅有另一个状态推导出
这里我们通过看这个题目其实不难发现,我们的f(n)的结果是由f(n-1)和f(n-2)两个共同决定的,而如果我们从状态来看,就是f(n)的状态是由f(n-1)+f(n-2)转化过来的。
这里我们就可以列出我们的状态转移方程了
f(n)=f(n-1)+f(n-2)
这下面的做题代码相对简单,小赵就不写了。
🚀4.多状态转移方程
我们前面的问题相对基础,我们大多数在考场上面临的问题,大多都是多状态转移方程。
我们来根据题目去学习这样的多状态的思想。
这道题目的难点就在于,我们每个屋子都有两个状态一个是偷的状态,一个是不偷的状态,那怎么办呢?那就分别记录。用一个数组记录当下这个房子偷的状态的最大金额,用另一个数组记录这个房子不偷的金额。
首先来看偷的状态:
我们偷的状态其实就是要由前面一个房子不偷的最大状态去转移过来,那么我们就可以列出式子
f(n)=g(n-1)+num(n)
然后我们在来看当下这个房子不偷的状态
这里就是由前面两种可能的状态转移过来,一种是偷,一种是不偷,那么我们这里就要选取最大值,也可以列出我们的状态转移方程
g(n)=max(f(n-1),g(n-1))
接着我们在进行一定的初始化工作,然后算出每个位置的值这道题就算结束了。
class Solution {
public:
int rob(vector<int>& nums) {
int numsize=nums.size();
if(numsize==1) return nums[0];
vector<int> f(numsize);
vector<int> g(numsize);
f[0]=nums[0];//初始化
g[0]=0;
for(int i=1;i<numsize;i++)//从左向右填表
{
f[i]=g[i-1]+nums[i];
g[i]=max(f[i-1],g[i-1]);
}
return max(f[numsize-1],g[numsize-1]);//最后返回最后一个房子偷和不偷两种情况中的较大值
}
};
🚀 5.蓝桥杯省模拟赛第二期第四题
蓝桥杯的这道题目和我们前面说的题目很像,但又略微有点不同,我们下面看题目就会知道其中的原因。
其实这里和我们之前的题目一样,我们这个数字可能由状态过来。
在考场的时候,我就是这样想的,但是随机又出现了一个问题就是这个最大整数的好像不止这一个情况可以过来。
那如果是这样玩那我们的dp方程就很难解决了,但是如果我们换一种思想就可以轻易解决这个问题,就是我用前一种状态直接推后一种状态(那么这样就是确定的只有三种),然后让他们在推的过程中取最小值。
int max_digit(int n)
{
int res = 0;
while (n)
{
res = max(res, n % 10);
n /= 10;
}
return res;
}//分离数位,返回最大值
int dp[2029];
int main()
{
memset(dp, 0x3f3f3f, sizeof(dp));//每个位置都是最大值
dp[1] = 0;//初始条件
for (int i = 1; i <= 2050; i++)
{
dp[i + 1] = min(dp[i + 1], dp[i] + 1);//代价1
dp[i + max_digit(i)] = min(dp[i + max_digit(i)], dp[i] + 3);//代价3
dp[2 * i] = min(dp[2 * i], dp[i] + 10);//代价10
}
cout << dp[2024];
return 0;
}
🚀6.结束语
好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵的博客中有什么地方不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。
如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,方便小赵及时改正,感谢大家支持!!!