洛谷P2858 奶牛零食 题解 区间DP入门题
题目大意:
约翰经常给产奶量高的奶牛发特殊津贴,于是很快奶牛们拥有了大笔不知该怎么花的钱.为此,约翰购置了 \(N(1 \le N \le 2000)\) 份美味的零食来卖给奶牛们.每天约翰售出一份零食.当然约翰希望这些零食全部售出后能得到最大的收益.这些零食有以下这些有趣的特性:
- 零食按照 \(1 \cdots N\) 编号,它们被排成一列放在一个很长的盒子里.盒子的两端都有开口,约翰每天可以从盒子的任一端取出最外面的一个.
- 与美酒与好吃的奶酪相似,这些零食储存得越久就越好吃.当然,这样约翰就可以把它们卖出更高的价钱.
- 每份零食的初始价值不一定相同.约翰进货时,第 \(i\) 份零食的初始价值为 \(V_i(1 \le V_i \le 1000)\) .
- 第 \(i\) 份零食如果在被买进后的第 \(a\) 天出售,则它的售价是 \(V_i \times a\) .
\(V_i\) 是从盒子顶端往下的第i份零食的初始价值.约翰告诉了你所有零食的初始价值,并希望你能帮他计算一下,在这些零食全被卖出后,他最多能得到多少钱.
解题思路:
我们定义状态 \(f[L][R]\) 为将区间 \([L,R]\) 依次去空能能够获得最多的钱。
那么我们可以发现我们的答案就是 \(f[1][n]\) ,那么怎么求解 \(f[1][n]\) 呢?先不急,听我细细道来~
假设我们现在要求解 \(f[L][R]\) ,那么我们可以发现,对于区间 \([L,R]\) ,我们取走一份零食的方案只有两种:
- 方案一:从左边取走 \(V_L\) ,然后状态变成了 \(f[L+1][R]\);
- 方案二:从右边取走 \(V_R\) ,然后状态变成了 \(f[L][R-1]\)。
对于区间 \([L,R]\) ,首先我们要确定我们取走的零食(无论是方案一还是方案二)是第几份取走的零食?
我们可以发现,\([L,R]\) 区间的左边有 \(L-1\) 份零食在之前被取走了,右边有 \(n-R\) 份零食在之前被取走了,所以我们现在取的零食是第 \(L-1 + n-R + 1 = n+L-R\) 份。
所以采用第一种方案能够获得的最多的钱是
\]
采用第二种方案能够获得的最多的钱是
\]
那我们现在要求解的状态 \(f[L][R]\) 应该是两种方案的较大值,所以我们可以得到最终的状态转移方程如下:
\]
当然,还需要注意的情况是我们的边界条件,即:区间长度为 \(1\) 时的情况,此时,对于所有的区间 \([i,i]\) ,第 \(i\) 份零食都是最后取走的(即第 \(n\) 份被取走的),所以
\]
基于上面的推导,我们可以通过记忆化搜索的形式实现我们的主要代码:
int dfs(int L, int R) {
if (f[L][R]) // 记忆化操作
return f[L][R];
if (L == R) // 边界条件
return V[L] * n;
return f[L][R] = max(dfs(L+1, R)+V[L]*(n+L-R), dfs(L, R-1)+V[R]*(n+L-R));
}
记忆化搜索的思想还是非常对应我们人脑的思考方式的,我们可以发现,这个程序主要分为三部分:
首先我们的 dfs(L,R) 就是为了返回 \(f[L][R]\),所以它:
- 首先判断是不是已经计算过了(通过 \(f[L][R]\) 是否为 \(0\) 来判断),如果已经计算过了直接发挥结果;
- 其次判断是不是边界条件(通过 \(L\) 是否等于 \(R\) 来判断),如果是边界条件直接返回 \(V_L \times n\);
- 最后计算并记录值,以便下一次计算的时候直接返回。
使用记忆化搜索的完整代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int n, V[maxn], f[maxn][maxn];
int dfs(int L, int R) {
if (f[L][R]) // 记忆化操作
return f[L][R];
if (L == R) // 边界条件
return V[L] * n;
return f[L][R] = max(dfs(L+1, R)+V[L]*(n+L-R), dfs(L, R-1)+V[R]*(n+L-R));
}
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> V[i];
cout << dfs(1, n) << endl;
return 0;
}
我们也可以采用一般形式来解决这个问题(一般形式和记忆化搜索形式的思路都是一样的,只不过一个是直接for循环顺着来,另一个是递归着来,要注意区分和类比)。
我们可以发现,大区间(即区间长度较大的区间)对应的状态都是通过小区间(即区间长度较小的区间)对应的状态推导出来的,所以我们只要从小到大遍历区间长度,再遍历区间左坐标,计算对应状态即可。
主要代码如下:
for (int l = 1; l <= n; l ++) { // 从小到大遍历区间长度l
for (int i = 1; i+l-1 <= n; i ++) { // 遍历区间左边界i
int j = i+l-1; // 通过左边界i和区间长度l获得区间右边界j
if (l == 1) f[i][j] = V[i]*n; // 边界条件直接返回结果
else f[i][j] = max(f[i+1][j] + V[i]*(n+i-j), f[i][j-1] + V[j]*(n+i-j)); // 否则,通过状态转移方程推导
}
}
一般形式的完整实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int n, V[maxn], f[maxn][maxn];
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> V[i];
for (int l = 1; l <= n; l ++) { // 从小到大遍历区间长度l
for (int i = 1; i+l-1 <= n; i ++) { // 遍历区间左边界i
int j = i+l-1; // 通过左边界i和区间长度l获得区间右边界j
if (l == 1) f[i][j] = V[i]*n; // 边界条件直接返回结果
else f[i][j] = max(f[i+1][j] + V[i]*(n+i-j), f[i][j-1] + V[j]*(n+i-j)); // 否则,通过状态转移方程推导
}
}
cout << f[1][n] << endl;
return 0;
}
洛谷P2858 奶牛零食 题解 区间DP入门题的更多相关文章
- 洛谷P2858奶牛零食 题解
题目 这个题一开始能看出来是一道动态规划的题目,但是并不知道如何写状态转移方程,但是我们可以想一想这个题应该是一道区间DP,而区间DP的特点就是状态转移方程一般跟该区间的左节点和右节点或者中间断点有关 ...
- 洛谷 P2858 奶牛零食
https://www.luogu.org/problemnew/show/P2858 毫无疑问区间dp. ![区间dp入门] 我们定义dp[i][j]表示从i到j的最大收益,显然我们需要利用比较小的 ...
- [洛谷p2858] 奶牛零食
题目链接: 点我 题目分析: 这是什么,区间dp吗?怎么大佬都在说区间dp的样子 完蛋区间dp都不知道是啥quq 于是使用了玄学的姿势A过了这道题 设dp[i][j][0]表示第i天,左边选了j个,当 ...
- luogu2858奶牛零食题解--区间DP
题目链接 https://www.luogu.org/problemnew/show/P2858 一句话题意: https://cn.vjudge.net/problem/POJ-3186#autho ...
- 洛谷P1220 关路灯 题解 区间DP
题目链接:https://www.luogu.com.cn/problem/P1220 本题涉及算法:区间DP. 我们一开始要做一些初始化操作,令: \(p[i]\) 表示第i个路灯的位置: \(w[ ...
- 洛谷P2719 搞笑世界杯 题解 概率DP入门
作者:zifeiy 标签:概率DP 题目链接:https://www.luogu.org/problem/P2719 我们设 f[n][m] 用于表示还剩下n张A类票m张B类票时最后两张票相同的概率, ...
- 洛谷 P1879 玉米田(状压DP入门题)
传送门 https://www.cnblogs.com/violet-acmer/p/9852294.html 题解: 相关变量解释: int M,N; int plant[maxn][maxn];/ ...
- 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)
洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...
- 题解——洛谷P4767 [IOI2000]邮局(区间DP)
这题是一道区间DP 思维难度主要集中在如何预处理距离上 由生活经验得,邮局放在中间显然最优 所以我们可以递推求出\( w[i][j] \)表示i,j之间放一个邮局得距离 然后设出状态转移方程 设\( ...
随机推荐
- Android 使用SystemBarTint设置状态栏颜色
做项目时,发现APP的状态栏是系统默认的颜色,突然想到,为啥别的APP是自己设置的颜色(和APP本身很相搭),于是也想给自己的APP设置系统状态栏的颜色,更加美美哒... 搜了下,发现原来设置状态栏居 ...
- intellij idea 搜索
. Ctrl+N 按名字搜索类 相当于eclipse的ctrl+shift+R,输入类名可以定位到这个类文件 就像idea在其它的搜索部分的表现一样,搜索类名也能对你所要搜索的内容多个部分进行匹配 甚 ...
- laravel多表登录出现路由调用错误
public function auth() { // Authentication Routes... $this->get('login', 'Auth\LoginController@sh ...
- 二叉堆&&左偏堆 代码实现
今天打算学习左偏堆,可是想起来自己二叉堆都没有看懂,于是就跑去回顾二叉堆了.发现以前看不懂的二叉堆,今天看起来特简单,随手就写好了一个堆了. 简单的说一下我对二叉堆操作的理解.我不从底层函数说上去,相 ...
- JavaScript跨域问题
通过实现Ajax通信的主要限制,来源于跨域安全策略.默认情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源.这种安全策略可以预防某些恶意行为.但是,实现合理的跨域请求对于开发某些浏览器应用程 ...
- Attention is all your need 谷歌的超强特征提取网络——Transformer
过年放了七天假,每年第一件事就是立一个flag——希望今年除了能够将技术学扎实之外,还希望能够将所学能够用来造福社会,好像flag立得有点大了.没关系,套用一句电影台词为自己开脱一下——人没有梦想,和 ...
- [转]在Windows中安装Memcached
Memcached是一个高并发的内存键值对缓存系统,它的主要作用是将数据库查询结果,内容,以及其它一些耗时的计算结果缓存到系统内存中,从而加速Web应用程序的响应速度. Memcached最开始是作为 ...
- tp框架使用心得(六)——分页查询
http://baijiahao.baidu.com/s?id=1578482537511010805&wfr=spider&for=pc 在用thinkphp中,对于新手手册中还是有 ...
- 2018-8-10-win10-uwp-slider-隐藏显示数值
title author date CreateTime categories win10 uwp slider 隐藏显示数值 lindexi 2018-08-10 19:17:19 +0800 20 ...
- 随机抽样 (numpy.random)
随机抽样 (numpy.random) 简单的随机数据 rand(d0, d1, ..., dn) 随机值 >>> np.random.rand(3,2) array([[ 0.14 ...