Prelude

快THUWC了,所以补一下以前的题。

真的是一道神题啊,网上的题解没几篇,而且还都看不懂,我做了一天才做出来。

传送到LOJ:(>人<;)


Solution

直接切入正题。

我们考虑区间dp,第一件事是离散化。

然后用\(g(i,j)\)表示消除完闭区间\([i,j]\)的最小费用。

然后呢?怎么转移?exm???

这时候会有一个非常自然的想法。

计算\(g(i,j)\)的时候,我们枚举两个数\(l,r\),然后保留下值在闭区间\([l,r]\)之内的所有数,先消除掉其他的数字,就只剩\([l,r]\)之内的数字了,再一次性消除掉她们。

时间复杂度\(O(n^5)\),但是显然是错的。

错在哪里呢?大概是错在下面这种情况,我懒得构造具体的反例了。

对于一组数字\(abcabca\),我们可以先消除掉中间的\(a\),再消除掉\(bcbc\),最后再消除掉\(aa\),在我们的dp里面似乎并没有考虑到这种情况。

因为\(aa\)是最后消除掉的,因此如果我们选择保留\(a\)的话,会保留下来所有的\(a\)。

我们太仁慈了,保留下来了\([l,r]\)之间的所有的数字,其实不一定要保留所有数字。

怎么办呢?

脑洞大开!

我们用\(f(i, j, l, r)\)表示,消除完在闭区间\([i,j]\)之内的,除了值在\([l,r]\)之间的所有数字。

注意,在\([l,r]\)之间的数字,可以消除,也可以不消除。

然后显然有这个东西:

$\Large g(i, j) = \min f(i, j, l, r)$
实际上就是枚举$l,r$嘛。
然后我们考虑$f(i, j, l, r)$如何转移。
当闭区间$[i,j]$内元素全部在$[l,r]$之间的时候,显然$f(i, j, l, r)=0$。
当闭区间$[i,j]$内元素全部不在$[l,r]$之间的时候,显然$f(i, j, l, r)=g(i, j)$。
$f(i, j, l, r)=g(i, j)$似乎构成了循环依赖?
那么,我们枚举$l,r$的时候,必须保证区间$[i,j]$内存在至少一个数字在$[l,r]$内,这样就不会有循环依赖了。
解决了$f(i, j, l, r)$的边界问题,接下来看如何转移。
像普通的区间dp一样,我们枚举区间的分裂点$k$,然后把区间$[i,j]$分裂成$[i,k]$和$[k+1,j]$两部分,递归做下去。
有式子:
$\Large f(i, j, l, r) = \min f(i, k, l, r) + f(k+1, j, l, r)$
感受一下,感觉似乎是能处理各种情况的?
但是实际上和刚刚的做法没有任何区别。
因为对于状态$f(i, j, l, r)$,我们仍然保留了$[l,r]$之间的所有数字,仍然是那么的仁慈。
我们需要加一种暴力斩掉所有数字的情况。
有式子:
$\Large f(i, j, l, r) = \min g(i, k) + f(k+1, j, l, r)$
仔细感受一下,这两个$f(i, j, l, r)$的转移式结合起来之后,就可以处理掉所有情况了!
时间复杂度仍然是$O(n^5)$。
实现采用记忆化搜索,效果棒棒哒~
真是一道神题啊。。。


Code

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <iostream> using namespace std;
const int N = 52;
const int W = 1010;
const int INF = 0x3f3f3f3f;
int _w; int bmin( int &a, int b ) {
return a = b < a ? b : a;
} int n, a, b, w[N];
int vis[W], num[N], m;
int f[N][N][N][N], g[N][N];
int F( int, int, int, int );
int G( int, int ); void discrete() {
for( int i = 1; i <= n; ++i )
vis[w[i]] = 1;
m = 1;
for( int i = 1; i < W; ++i )
if( vis[i] )
vis[i] = m, num[m++] = i;
--m;
for( int i = 1; i <= n; ++i )
w[i] = vis[w[i]];
} bool contain( int i, int j, int l, int r ) {
for( int p = i; p <= j; ++p )
if( w[p] >= l && w[p] <= r )
return true;
return false;
} bool all( int i, int j, int l, int r ) {
for( int p = i; p <= j; ++p )
if( w[p] < l || w[p] > r )
return false;
return true;
} int F( int i, int j, int l, int r ) {
int &now = f[i][j][l][r];
if( now != -1 ) return now;
if( all(i, j, l, r) ) return now = 0;
if( !contain(i, j, l, r) ) return now = G(i, j);
now = INF;
for( int k = i; k < j; ++k ) {
bmin( now, F(i, k, l, r) + F(k+1, j, l, r) );
bmin( now, G(i, k) + F(k+1, j, l, r) );
}
// printf( "f[%d][%d][%d][%d] = %d\n", i, j, l, r, now );
return now;
} int G( int i, int j ) {
int &now = g[i][j];
if( now != -1 ) return now;
now = INF;
for( int l = 1; l <= m; ++l )
for( int r = l; r <= m; ++r )
if( contain(i, j, l, r) ) {
int u = num[l], v = num[r];
bmin( now, F(i, j, l, r) + (v-u)*(v-u)*b + a );
}
// printf( "g[%d][%d] = %d\n", i, j, now );
return now;
} int main() {
cin >> n >> a >> b;
for( int i = 1; i <= n; ++i )
cin >> w[i];
discrete();
memset(f, -1, sizeof f);
memset(g, -1, sizeof g);
printf( "%d\n", G(1, n) );
return 0;
}

【题解】【THUSC 2016】成绩单 LOJ 2292 区间dp的更多相关文章

  1. loj 1031(区间dp+记忆化搜索)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1031 思路:dp[i][j]表示从区间i-j中能取得的最大值,然后就是枚举分割点了. ...

  2. BZOJ 1996: [Hnoi2010]chorus 合唱队(区间dp)

    题目: https://www.lydsy.com/JudgeOnline/problem.php?id=1996 题解: 这题刚拿到手的时候一脸懵逼qwq,经过思考与分析(看题解),发现是一道区间d ...

  3. 「USACO16OPEN」「LuoguP3146」248(区间dp

    题目描述 Bessie likes downloading games to play on her cell phone, even though she doesfind the small to ...

  4. SPOJ MIXTURES 区间dp

    Harry Potter has n mixtures in front of him, arranged in a row. Each mixture has one of 100 differen ...

  5. UVA1630 Folding 区间DP

    Folding Description   Bill is trying to compactly represent sequences of capital alphabetic characte ...

  6. LOJ 2292 「THUSC 2016」成绩单——区间DP

    题目:https://loj.ac/problem/2292 直接 DP 很难做,主要是有那种 “一个区间内部有很多个别的区间” 的情况. 自己想了一番枚举 max-min 的最大限制,然后在该基础上 ...

  7. loj 2292「THUSC 2016」成绩单

    loj 看着就很区间dp,所以考虑求\(f_{i,j}\)表示区间\([i,j]\)的答案.注意到贡献答案的方式是每次选一个连续段,拿走后剩下的段拼起来继续段,所以转移就考虑从最后一次选的方法转移过来 ...

  8. 2016 ACM/ICPC Asia Regional Shenyang Online 1009/HDU 5900 区间dp

    QSC and Master Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...

  9. 【bzoj4897】[Thu Summer Camp2016]成绩单 区间dp

    题目描述 给你一个数列,每次你可以选择连续的一段,付出 $a+b\times 极差^2$ 的代价将其删去,剩余部分拼到一起成为新的数列继续进行此操作.求将原序列全部删去需要的最小总代价是多少. 输入 ...

随机推荐

  1. python正则表达式,以及应用[下载图片]

    regular expresion由一系列特定字符及其组合成的字符串,用来对目标字符串进行过滤操作.. re相关知识点 python正则表达式库为re,用import re导入,在然后用re.comp ...

  2. Windows搭建python开发环境

    python你不去认识它,可能没什么,一旦你认识了它,你就会爱上它 基本概念Python(英语发音:/ˈpaɪθən/), 是一种面向对象.解释型计算机程序设计语言,由Guido van Rossum ...

  3. SpringBoot集成dubbo实例

    项目总览图: 最下面有项目的pom,具体内容: 项目运行注意事项: 先启动 provider, 将providers.xml中 port 先修改为20187 执行test目录 下的DubboProvi ...

  4. 使用socket发送http请求(get/post)

    手动发送http请求 解释说明 https://blog.csdn.net/zhangliang_571/article/details/23508953 http://www.cnblogs.com ...

  5. Linux读书笔记第一、二章

    第一章    Linux内核简介 1.1Unix历史 Unix特点:1.很简洁 2.所有东西都被当成文件对待 3.Unix内核和相关的系统工具软件都是用C语言编写而成 4.进程创建非常迅速 1.2追寻 ...

  6. Linux 目录结构及文件基本操作

    Linux 目录结构及文件基本操作 实验介绍 1.Linux 的文件组织目录结构. 2.相对路径和绝对路径. 3.对文件的移动.复制.重命名.编辑等操作. 一.Linux 目录结构 在讲 Linux ...

  7. 第三周linux学习

    实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用 ...

  8. 福大软工1816 · 评分结果 · beta冲刺总评

    作业地址:beta答辩总结.beta冲刺7.beta冲刺6.beta冲刺5.beta冲刺4.beta冲刺3.beta冲刺2.beta冲刺1.beta冲刺前准备 作业提交准则 按时交 - 有分 晚交 - ...

  9. 在CANopen网络中通过LSS服务设置节点地址和网络波特率

    CANopen专有个子协议用来描述怎样去通过网络设置节点地址和波特率,就是CiA DSP-305,大伙都叫LSS协议,是Layer Setting Services的缩写,不太好翻译,也许可以翻译成底 ...

  10. 微信小程序demo——入门级(附源码)

    最近小程序又蠢蠢欲动,出了一个公众号绑定小程序功能,目测不错,就看了下微信小程序文档,顺便写了几行代码,后续有空会持续更新维护. 源码:https://github.com/SibreiaDante/ ...