| |
|
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
| -> 数据结构与算法 -> dp干了我吧 -> 正文阅读 |
|
|
[数据结构与算法]dp干了我吧 |
|
说到动态规划,我们应该先了解一个概念: 无后效性:无后效性是指如果在某个阶段上过程的状态已知,则从此阶段以后过程的发展变化仅与此阶段的状态有关,而与过程在此阶段以前的阶段所经历过的状态无关。利用动态规划方法求解多阶段决策过程问题,过程的状态必须具备无后效性。 说通俗一点就是要解决一个问题的时候,其子问题必须全部被解决。 我认为动态规划分为两种类型:第一种:用来记录递归变量的变化状态,写出递归基本上就写出了动态规划。这种情况一般情况下第一个位置是用来遍历的,第二个,第三个等位置是题目的限制条件,而dp表本身的含义是在第一个参数和第二个参数的情况下,我们的答案是多少。以上所说我认为dp表的含义和题目的限制条件没有强关联性才可以。什么叫强关联性,例如01背包问题,题目的限制条件是背包的重量不可以超重,而要求是最大价值。这就不具有强关联性。再反观砝码问题,题目没有限制条件,只是问你能称多少种重量,也就是说,我的dp表确实需要第二个参数,但是这第二个参数并不是限制条件,这种情况下,dp表一般是第二个位置是否保存了某个值为参考标准,有就是1,没有就是0。这样的题目属于第二种,我们认为dp表不是记录递归变量的变化状态的。而是它本身就跟结果有关。 这些理论只是初步想法,我们用题目来验证: 砝码问题题目描述你有一架天平和 N 个砝码,这 N 个砝码重量依次是 W1, W2, · · · , WN。 请你计算一共可以称出多少种不同的重量? 注意砝码可以放在天平两边。 输入格式输入的第一行包含一个整数 N。 第二行包含 N 个整数:W1, W2, W3, · · · , WN。 样例输入输出【样例输入】 3 1 4 6 【样例输出】 10 思路分析:一看到题目,我的想法就是从暴力递归到动态规划,一个index变量用来遍历,再用一个限制条件。但是突然我发现题目的限制条件不存在,只是说随意怎么放砝码,能称量出多少种重量。也就是说变量有2个,一个是index下标,一个是重物的重量,但是重物的重量没有任何限制。因此我想到了暴力递归dfs,再用一个hashmap来保存这个重量是否出现过,然后计算一共出现了多少种重量。因此有了以下代码 我们定义递归含义是:在[0, index]区间范围之内的砝码可以称量出重量sum吗? 或者说,这个递归没有严格的含义,它就是一共穷举行为,一共dfs遍历。 #include<iostream>
#include<unordered_map>
#include<math.h>
using namespace std;
unordered_map<int, int> m;
int N;
void dfs(int index, int sum, int w[])//index是下标遍历,sum是目前重物的重量
{
? ?if(index == N)
? {
? ? ? ?m[sum]++;
? ? ? ?return ;
? }
? ?dfs(index + 1, sum + w[index], w);//放砝码目前重物改变
? ?dfs(index + 1, sum, w);//我不要这个重物
? ?dfs(index + 1, fabs(sum - w[index]), w);
}
int main()
{
? ?cin >> N;
? ?int*w = new int[N + 1];
? ?int sum = 0;
? ?for(int i = 0; i < N; ++i)
? {
? ? ? ?cin >> w[i];
? ? ? ?sum += w[i];
? }
? ?dfs(0, 0, w);
? ?int ans = 0;
? ?for(int rest = 1; rest <= sum; rest++)
? {
? ? ? ?if(m[rest] >= 1)
? ? ? {
? ? ? ? ? ?ans++;
? ? ? }
? }
? ?cout << ans <<endl;
? ?return 0;
}
当然,暴力会超出时间限制的。我们是不是可以考虑一下剪枝?我觉得没必要,这道题也不好剪枝。 有几个值得注意的点: 1.为什么要加上绝对值? 答:因为就算重量减成负数了,那么把重物放在另外一边就可以了,这是可以自由选择的,我们没有规定重物必须在哪一边。sum + w[index]表示的是我可以通过加砝码,我不知道加在哪一边,可以算出sum + w[index]重量的物品,-也是一样,同理。 接下来使用动态规划的思路: 定义dp[i] [j],i表示index,用于遍历,j表示目前可以称量出来的重物,dp表示能否装得下这个重物,装得下就算1,装不下就算0. 从第一个角度来看 我在[0, 0]范围之内可以称量出w[0]的重物,按照测试用例也就是1 [0, 1]范围内可以称量出1, 1 + 4, |1 - 4| 4也就是1 3 5 4 [0, 2]范围额可以称量出1 3 5(继承过来) 3 + 4 |3 - 4| 5 + 4 |5 - 4|也就是1 3 5 7 9 4.... ....... 以此类推,按照这个思路,我们写出动态规划的代码:
总结:这个动态规划很诡异,它的dp含义记录的是是否存在dp的第二个参数,这对应我们所说的第二种dp吧。 走楼梯进阶题目描述楼梯有 nn 阶,上楼可以一步上一阶,也可以一步上二阶。 但你不能连续三步都走两阶,计算走到第nn阶共有多少种不同的走法。 输入格式一行,一个数字,表示nn。 输出格式输出走楼梯的方式总数。 暴力思路:
这个暴力时间很长,不做过多的阐述,很简单。 动态规划解析: 一维dp错误案例剖析:
错误思路:我的可能性是走一级台阶,或者两级台阶的可能性之和,再减去一次性走6级台阶,但是这个思路严重错误了,因为你没有考虑到当一次性走6级台阶的index - 6的时候可能它本身就已经走了一级或者两级2级台阶了,所以如果我们这么减的话那么会减多了情况。 也就是说,我们要让它在index - 7的位置必须走一步到达index - 6的位置再连续走6步才可以。 代码如下:
但是我们发现这样有点牵强,这个一维dp 因此我们这样写: 利用这个思路:
这里借用一下大佬的思路,这不是我的思路,这不是我的思路,这不是我的思路 二维dp我们用第二个参数x,来记录是否连续走了x阶台阶。 代码如下:
这里有几个问题: 1.我们为什么是Index = 2; index <= n;index++? 答:因为我们知道index = 0或者1的时候x一定为0,这样的话状态是清楚的,子问题是被解决的,问题是发散出去的。 但是如果我们写index = n; index >=0; index--的话,那么最后一个状态我们的子问题是为止的,状态为止,子问题无法被解决,所以是错误的。 总结一维dp需要思维,二维dp的第二个参数是记录状态的,是限制条件。很难从暴力递归到动态规划。 |
|
|
|
|
| 上一篇文章 下一篇文章 查看所有文章 |
|
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
| 360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年12日历 | -2025/12/21 9:40:10- |
|
| 网站联系: qq:121756557 email:121756557@qq.com IT数码 |