经典DP 洛谷p1880 石子合并
https://www.luogu.org/problemnew/show/P1880 题目
题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入输出格式
输入格式:
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:
输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
输出样例#1
54
卡了我蛮久的。。各种弱智错误。。分享经验,记录自我!
首先思想:
贪心:每次都选取最小的两个进行合并,这样就能保证得到局部最优解,也就是每次合并的值都是最小的
DP:什么是dp呢,在我看来就是通过一种数学统计的方法将小的状态转移到大状态(妙用max,min),事实上这感觉像是一个记忆化的枚举思想(只是感觉像,帮助理解,这是个人的理解,不是真正的定义,逃)
说完了区别,那么dp最重要的来了
状态转移方程
要找到状态转移方程,就要yy出,这个大答案,可以由什么样的小答案通过题意的操作得到
比如这道题,属于区间dp
什么意思呢
它的大答案,就是大区间的答案,可以由小区间的答案,通过合并加分的操作进行统计 (对应我的话,应该不难)
有了yy的方向,就要大胆写方程,这道题的方程其实并不难找:
dpmax[i][j] = max(dpmax[i][j], dpmax[i][k] + dpmax[k + ][j] + sum(i, j));
dpmin[i][j] = min(dpmin[i][j], dpmin[i][k] + dpmin[k + ][j] + sum(i, j));
进行解释:区间i,j的最大(最小)值 == 区间i,k的得分的最大(最小)值 + 区间k + 1, j的得分的最大(最小)值 + 本次合并的得分(下面的都用sum)
到这里没人不理解叭
好了,方程出来了,想办法实现,要先搞清楚这循环怎么写
dp[i,j] 很简单,就直接两个for从1到n,从j到n(虽然下面并不是这样写的,不过先别急,这是可以算是一个dp入门教程,但不是入门题啊)
于是就有
for (int i = ; i <= n; i++)
for (int j = i; j <= n; j++)
dp[i][j] = max(dp[i][j], ??????);
?怎么办,k去哪里了
别急,先想想k是什么,要怎么开始,到哪里结束
emmmm 直接说吧,k,应该是在dp[i][j] 时 从i到j的一个我认为可以叫枚举的(极其不负责)东西叭,反正就是每个区间都要用for进行尝试
于是
for (int i = ; i <= n; i++)
for (int j = i; j <= n; j++)
for (int k = i; k <= j; k++)
dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + ][j] + sum(i, j));
但是这里我们会发现一个问题
eg: dp[1][3] = max(dp[1][3], dp[1][2] + dp[2][3] + sum(1, 3)); 当进行到这里时,会发现dp[2][3]好像从来都还没算过,难道后面会算吗。答案是会算,但是,dp[1][[3]已经不会再更新了,所以这样统计答案肯定会出问题
那么是不是方程出了问题,没有啊,很完美啊!!!
确实,非常完美(毕竟是题解)
那么这个问题怎么解决呢?
回到一开始的思想,我们要先算完所有小区间的答案,再去统计大区间的答案对不对?那么是不是从长度为1的区间开始运算?如果到这里都能够理解的话,这题就基本上成功了,但是AC嘛,你懂的
好,那么重新开始
第一层循环用长度,从1开始
然后第二层用要统计的区间的起点,那是不是就不需要终点了,因为终点就等于长度加起点
第三层循环统计i到j区间内拆分的每一种情况
如下:
for (int len = ; len < n; len++)
for (int i = ; i <= n; i++)
{
int j = i + len;
for (int k = i; k <= j; k++)
dp[i][j] = max(dp[i][j], dp[i][k]+ dp[k + ][j] + sum(i, j);
}
非常接近答案了,各位加油
现在大家可以去翻一下最后ac代码,发现dp这个主要的循环基本上一致
然后,回到题面,发现这是个环形的石堆???瞬间脑子里蹦出指针啊,int p啊什么的,但是一激动,一写,啊,好难啊好烦啊
这里就有一个对付环经常用到的思想,把数组复制一遍,扩展成两倍
例如:
A B C D是题目给的堆
复制一遍 A B C D A B C D
如果用这个复制后的数组去dp,会发生什么,你是不是就统计到了D 到 A 或者D 到 B 或者D 到 C 的答案.....
为什么这样是正确的呢,大部分人应该都理解,但是还是想说一下
我们这样想,如果它是环的话,是不是A 与 D就能合并(就是相邻的),是不是就会统计出一个答案,我们需要的其实就是这个答案的数值,不用管什么乱七八糟的想法,要数值大小就ok(有人就会有奇奇怪怪的想法)
这个理解了,就去实现,直接看下面的最终代码叭,一看就懂,只要不走神
那么问题又来了,最后答案输出什么呢??dp[1][n] ??
肯定不对,那复制和没复制有什么区别。。
所以就要checkans(检查答案。。。),怎么检查?其实很简单,只要把每个长度为len(即n - 1)的dp[i][j]都检查一下,找出最大或者最小的那个,就是答案了
为什么这样又是对的??只因为检查的长的为len
所有的问题都解决了,然后简单优化(什么平行四边形优化都是dalao才会的东西。。)一下叭。
统计sum的优化--前缀和,在dp输入时统计一下前i个石子的和,然后用的时候用前j个的和减掉前i个的和,就是sum(i, j)了
再考虑一下这个问题,复制数组的时候最后一位的复制好像并没有用到?
eg:
A B C D A B C D
统计第一个D到第二个D的答案?不需要啊,长度超了,第一层循环限制了长度,根本访问不到对吧
统计第二个A到第二个D的答案?也不要啊,这跟前面的第一个A到第二个D重复了,其他的其实也有重复,但是,我们需要D到AorBorC,所以其他的不需要删
最后得出,最后一位其实不用复制,没用,浪费(其实这个不算什么优化,只是分享一下某位dalao的一个想法,觉得很有道理)
最后大家完成代码是注意细节,什么max,min的初始化啊,循环的边界啊(我的代码的边界应该不完全恰当懒得检查了,但是不会影响答案,得益于第一层循环的长度限制)
上代码,祝大家acacacacacacac
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int N = ;
int n, a[N], s[N], dpmin[N][N], dpmax[N][N], ansmax = -, ansmin = ;
inline int sum(int x, int y)
{
return s[y] - s[x - ];
}
int main(){
scanf("%d", &n);
for (int i = ; i <= n; i++) scanf("%d", &a[i]);
for (int i = ; i <= n; i++) a[i + n] = a[i];
for (int i = ; i <= n * ; i++) s[i] = s[i - ] + a[i];
/*
for (int i = 1; i <= n * 2; i++)
dpmax[i][i] = dpmin[i][i] = 0;
for (int i = 1; i <= n * 2; i++)
dpmax[i][i] = dpmin[i][i] = a[i] + a[i + 1];
*/
for (int len = ; len < n; len++)
for (int i = ; i + len <= * n - ; i++)
{
int j = i + len;
dpmin[i][j] = ;
for (int k = i; k < j; k++)
{
dpmax[i][j] = max(dpmax[i][j], dpmax[i][k] + dpmax[k + ][j] + sum(i, j));
dpmin[i][j] = min(dpmin[i][j], dpmin[i][k] + dpmin[k + ][j] + sum(i, j));
}
} for (int i = ; i < n + ; i++) ansmax = max(ansmax, dpmax[i][i + n - ]);
for (int i = ; i < n + ; i++) ansmin = min(ansmin, dpmin[i][i + n - ]); // for (int i = 1; i < n * 2; i++)
// for (int j = 1; j < n * 2; j++)
// {
// cout << "dpmin" << i << ", " << j << " = " << dpmin[i][j] << endl;
// }
printf("%d\n%d", ansmin, ansmax);
return ;
}
感谢阅读
经典DP 洛谷p1880 石子合并的更多相关文章
- 洛谷P1880 石子合并(区间DP)(环形DP)
To 洛谷.1880 石子合并 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1 ...
- 洛谷P1880 石子合并(环形石子合并 区间DP)
题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计算出将N堆石子合并成1 ...
- 洛谷 P1880 石子合并
题目描述 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计算出将N堆石子合并成1 ...
- 洛谷P1880 石子合并
经典水题....... 断环为链长度乘二,求前缀和区间DP. #include <cstdio> #include <cstring> #include <algorit ...
- [codevs1048]石子归并&[codevs2102][洛谷P1880]石子归并加强版
codevs1048: 题目大意:有n堆石子排成一列,每次可合并相邻两堆,代价为两堆的重量之和,求把他们合并成一堆的最小代价. 解题思路:经典区间dp.设$f[i][j]$表示合并i~j的石子需要的最 ...
- 洛谷 P1080 石子合并 ( 区间DP )
题意 : 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分.试设计出1个算法,计算出将N堆石子合并成1堆 ...
- 洛谷P1880 [NOI1995]石子合并 纪中21日c组T4 2119. 【2016-12-30普及组模拟】环状石子归并
洛谷P1880 石子合并 纪中2119. 环状石子归并 洛谷传送门 题目描述1 在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石 ...
- 【洛谷】P1880 石子合并
P1880 石子合并 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计 ...
- P1880 石子合并
P1880 石子合并 题目描述 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分. 试设计出1个算法,计 ...
随机推荐
- qt学习(二) buttong layout spinbox slider 示例
开启qt5 creator 新建项目 qt widgets 改写main.cpp #include "mainwindow.h" #include <QApplication ...
- Java程序设计16——Annotatio注释
Annotation是代码里的特殊标记,这些标记可以在编译.类加载.运行时被读取,并执行相应的处理.通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息.代 ...
- Oracle学习笔记(十一)
例外: 例外是程序设计语言提供的一种功能,用来增强程序的健壮性和容错性. 例外分为:系统例外自定义例外 系统例外分为:No_data_found(没有找到数据).Too_many_rows(selec ...
- spark使用idea以client模式提交应用到standalone集群
使用idea以client方式提交代码到standalone集群非常简单. 1.首先有一个部署好且已经启动的standalone集群 --------------------------------- ...
- UVaLive 3126 Taxi Cab Scheme (最小路径覆盖)
题意:有 n 个客人,要从 si 到 ti,每个人有一个出发时间,现在让你安排最少和出租车去接,在接客人时至少要提前一分钟到达客人的出发地点. 析:把每个客人看成一个结点,然后如果用同一个出租车接的话 ...
- thinkphp整合系列之phpexcel导入excel数据
一:导入phpexcel /ThinkPHP/Library/Vendor/PHPExcel 二:导入excel的函数 /** * 导入excel文件 * @param string $file ex ...
- oracle树形语句
oracle树查询的最重要的就是select…start with…connect by…prior语法了.依托于该语法,我们可以将一个表形结构的以树的顺序列出来.在下面列述了oracle中树型查询的 ...
- 还原bak到localdb的问题:The logical database file cannot be found ldf
主要环境相关因素:win7,ms sql 2012,ms localdb,msms 2012. 步骤: 1,让DBA给一个bak文件到本地来做测试,DBA按自己的工作流程得到bak文件. 2,在msm ...
- Oracle EBS Color 色彩设置
Oracle EBS配色方案的截图 If the Java Look and Feel profile option is set to Oracle, the Java Color Scheme c ...
- 咏南BS开发框架
咏南BS开发框架 开发工具为:DELPHI 2006及以上版本.欢迎索取演示. 通用的三级权限管理. 登录验证 输入帐号和密码验证登录系统. 功能菜单 在这里设置好菜单,系统运行的时候会根据菜单设置动 ...