题目描述

小火车虽然很穷,但是他还是得送礼物给妹子,所以他前往了二次元寻找不需要钱的礼物。

小火车准备玩玩二次元的游戏,游戏当然是在一个二维网格中展开的,网格大小是\(n\times m\)的,某些格子是好的,其余的则是不好的。每次你可以选择最底层(也就是第\(n\)层)的某两个相邻的列,并消掉最底下的至多三个格子,并且这两列都得有格子被消掉(也就是\(L\)型或者反着的\(L\)型),消掉格子以后上面的格子会掉落下来。当然最上面的空位会用不好的格子填满。

小火车想得到所有的好格子送给妹子,请问至少得消多少次呢?

输入

第一行两个整数\(n,m\),表示网格大小。

接下来\(n\)行每行一个长度为\(m\)的字符串,表示这个网格,如果为*则是好格子,为#就不是。

输出

一行一个整数表示答案。

样例

样例输入

3 2
#*
*#
##

样例输出

2

数据范围

对于\(20\%\)的数据\(n,m<=5\)

对于\(50\%\)的数据满足\(n,m<=500\)

对于\(100\%\)的数据满足\(2<=n,m<=2000\)

题解

诈尸啦诈尸啦。

唔……全场码量最小的题。(然而也是唯一没想出来的题)

方法很多,不过有几个很玄幻,所以我采用的是其中一种巧妙、易懂又好写的方法(没有耐心的可直接看解法\(4\))。

解法\(0\):暴力模拟+搜索,期望得分:\(20pts\)。

解法\(1\):看出来这个和好格子的分布没关系,只与每一列最高的好格子有关系,然后\(f[i][j]\)表示前\(i\)列中,\(i-1\)列都消除完了,第\(i\)列恰好消除了\(j\)个最少需要花费多少步,暴力\(DP\)枚举第\(i\)列的\(j\)个是放置了\(k\)个反\(L\)型,可以轻松计算出\(L\)型的个数为\(j-2k\),暴力\(DP\),复杂度\(O(n^3)\),期望得分:\(50pts\)。常数优秀的选手(真实案例:滚动数组+每次只枚举到这一列的最大高度+枚举反\(L\)型而不是枚举\(L\)型获得\(\frac{1}{2}\)的常数)可以获得\(100pts\)。(弥天大雾

解法\(2\):考虑优化解法\(1\),找最优决策点太慢了,我们随意脑补一下就知道函数值和\(k\)肯定是一个单谷函数的关系,因为反\(L\)型太多会造成自己的浪费,太少又会导致前面的浪费,感性理解可得单谷性。所以三分——等等,别忘了——这是整数,如果最后算出来相等,是无法得知该往哪边靠的。如图,你不知道你现在是红还是蓝还是绿……



所以引入一个玄学操作:当\(l、r\)接近到一定程度时,就开始暴算。这个算法一看就非常玄学,你要是仔细拿捏这个度,理论上也是能过的(其实也是能过的),时间复杂度\(O(n^2\log n)\),期望得分:\(??pts\)。

解法\(3\):考虑其他优化,大佬\(Freopen\)提供了一个单调队列优化。容易发现,当相邻的两列比例在\([\frac{1}{2},2]\)之间时,最优方案是混合使用\(L\)型和反\(L\)型,总花费\(\left\lceil\frac{x+y}{3}\right\rceil\),然后枚举第\(i\)列与第\(i-1\)列一起被消去的部分长度为多少,对第\(i-1\)列被消去的部分进行讨论。如果是比例小于\(\frac{1}{2}\)或者大于\(2\),那么就是全部都放的是\(L\)型或者反\(L\)型,这个很好算,否则是两个混搭起来,也就是第二维下标在当前枚举的第\(i\)列被消去的高度的一半和两倍之间的上一列的\(dp\)值,这一坨,由于要整除\(3\)再上取整,为了避免误差,我们按照对\(3\)取余的值分成三坨,这样每一坨内部的就可以用单调队列搞。当更改第\(i\)列的时候,就按照常规操作更新单调对列,这样复杂度是\(O(n^2)\),期望得分:\(100pts\)。

是不是没听懂?我也没听懂。这个方法随口\(bb\)还是很容易说的,但是写起来可能就没有那么容易了,因此需要更简单的方法。

解法\(4\):我们看到解法\(2\)是不是感觉很不爽,明明知道一个性质却不能用……于是我们继续思考有没有能够一起使用的性质?

首先,可以显而易见地发现,当\(j\)增大时,\(f[i][j]\)的最优决策点\(k\)是不会变小的,因为第\(i\)列需要消除更多了,如果反\(L\)型的变少是对消除不利的,会造成更多的浪费。那么我们考虑暴算,利用解法\(2\)的单谷性,一旦暴算到一个决策点使得答案更差,那么后面必然不可能更优了,直接退出。再利用决策点\(k\)单调性,当\(j\)增加的时候只需要接着上一次的\(k\)继续暴算就行了,因此第三维的复杂度均摊下来是\(O(1)\)的,代码非常好写,时间复杂度\(O(n^2)\),期望得分:\(100pts\)。

\(Code:\)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 2005
#define inf 0x3f3f3f3f
void Read(int &p)
{
p = 0;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar());
for (; c >= '0' && c <= '9'; c = getchar())p = p * 10 + c - '0';
}
int n, m, h[N];
int f[N][N];
char S[N][N];
int main()
{
Read(n), Read(m);
for (int i = 1; i <= n; i++)
{
scanf("%s", S[i] + 1);
for (int j = 1; j <= m; j++)
if (S[i][j] == '*')
h[j] = max(h[j], n - i + 1);
}
memset(f, 0x3f, sizeof(f));
f[0][0] = 0;
//f[i][j]表示前i-1列已经被消除完,第i列被消除了j个,最少花费的步数
for (int i = 1; i <= m; i++)
{
int lst = 0;
for (int j = 0; j <= h[i]; j++)
{
for (int k = lst; 2 * k <= j; k++)//k表示第i列用了多少个J型,显然随着j的增大,k必定不会减少
{
int w = f[i - 1][max(0, h[i - 1] - k - 2 * (j - 2 * k))] + k + (j - 2 * k);
if (w > f[i][j])break;
f[i][j] = w;
lst = k;
}
}
}
printf("%d\n", f[m][h[m]]);
}

「模拟赛20190327」 第二题 DP+决策单调性优化的更多相关文章

  1. Newnode's NOI(P?)模拟赛 第二题 dp决策单调优化

    其实直接暴力O(n3)DP+O2O(n^3)DP+O_2O(n3)DP+O2​优化能过- CODE O(n3)O(n^3)O(n3) 先来个O(n3)O(n^3)O(n3)暴力DP(开了O2O_2O2 ...

  2. 「模拟赛20191019」A 简单DP

    题目描述 给一个\(n\times m\)的网格,每个格子上有一个小写字母. 对于所有从左上角\((1,1)\)到右下角\((n,m)\)只向下或向右走的路径构成的集合,判断是否存在两条走法不同的路径 ...

  3. 「模拟赛20191019」B 容斥原理+DP计数

    题目描述 将\(n\times n\)的网格黑白染色,使得不存在任意一行.任意一列.任意一条大对角线的所有格子同色,求方案数对\(998244353\)取模的结果. 输入 一行一个整数\(n\). 输 ...

  4. loj #6039 「雅礼集训 2017 Day5」珠宝 分组背包 决策单调性优化

    LINK:珠宝 去年在某个oj上写过这道题 当时懵懂无知wa的不省人事 终于发现这个东西原来是有决策单调性的. 可以发现是一个01背包 但是过不了 冷静分析 01背包的复杂度有下界 如果过不了说明必然 ...

  5. [NOI2009]诗人小G(dp + 决策单调性优化)

    题意 有一个长度为 \(n\) 的序列 \(A\) 和常数 \(L, P\) ,你需要将它分成若干段,每 \(P\) 一段的代价为 \(| \sum ( A_i ) − L|^P\) ,求最小代价的划 ...

  6. 「模拟赛20191019」C 推式子+贪心+树状数组

    题目描述 给定一棵\(n\)个点的有根树,根节点编号为\(1\),点有点权. 定义\(d(v)\)表示\(v\)到\(1\)的路径上的边数. 定义\(f(v,u)\)在\(v<u\)且\(v\) ...

  7. 「模拟赛20181025」御风剑术 博弈论+DP简单优化

    题目描述 Yasuo 和Riven对一排\(n\)个假人开始练习.斩杀第\(i\)个假人会得到\(c_i\)个精粹.双方轮流出招,他们在练习中互相学习,所以他们的剑术越来越强.基于对方上一次斩杀的假人 ...

  8. 「模拟赛20180306」回忆树 memory LCA+KMP+AC自动机+树状数组

    题目描述 回忆树是一棵树,树边上有小写字母. 一次回忆是这样的:你想起过往,触及心底--唔,不对,我们要说题目. 这题中我们认为回忆是这样的:给定 \(2\) 个点 \(u,v\) (\(u\) 可能 ...

  9. 「模拟赛 2018-11-02」T3 老大 解题报告

    老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图),由于新建的办公室太大以 ...

随机推荐

  1. python学习——练习题(1)

    """ 题目:有四个数字:1.2.3.4,能组成多少个互不相同且无重复数字的三位数?各是多少? """ import itertools d ...

  2. 修改eclipse默认workspace

    三种方法 (只改其一可能无效,最好都试试) 1. 修改exlipse安装目录下\configuration\.settings\org.eclipse.ui.ide.prefs文件,修改RECENT_ ...

  3. 缓存数据库redis

    什么是Redis? Redis是一个TCP服务器,支持请求/响应协议. 在Redis中,请求通过以下步骤完成: 客户端向服务器发送查询,并从套接字读取,通常以阻塞的方式,用于服务器响应. 服务器处理命 ...

  4. NUnit属性

    TestFixture:它标记一个类包含测试,申明该类是用来测试的.一般用在class的定义之前: Test一般是放在method之前,表示对该方法的测试:如前一篇文章所示的class. SetUp/ ...

  5. Python基本数据类型之字符串、数字、布尔

     一.数据类型种类 Python中基本数据类型主要有以下几类: Number(数字) String(字符串) Bool (布尔) List(列表) Tuple(元组) Sets(集合) Diction ...

  6. MSSQL列记录合并

    创建表及插入数据 If OBJECT_ID(N'Demo') Is Not Null Begin Drop Table Demo End Else Begin Create Table Demo( A ...

  7. cocos2d-x 在vs2010下的环境配置

    cocos2d-x编译完成之后生成了一堆动态库,在window下分别为.dll , .lib ,其中*lib是在编译的时候需要链接的,*.dll是在运行的时候依赖的.所以当我们创建一个cocos2d- ...

  8. IFC文件解析

    什么是IFC? EXPRESS语言与IFC体系 一.IFC 1.IFC简介 IFC是一个数据交换标准, 用于不同系统交换和共享数据.当需要多个软件协同完成任务时, 不同系统之间就会出现数据交换和共享的 ...

  9. linux的deamon后台运行

    有的时候需要将程序一直跑在后台,比如一些服务类代码,或者一些监控类代码.使用deamon是正确的一种思路. 以前我们在看<unix环境高级编程>的时候,有专门的整章详细介绍如何编写一个后台 ...

  10. php手机号正则

    preg_match("/^1[34578]{1}\d{9}$/", $phone)