update in 2019.1.21 优化了一下文中年代久远的代码 的格式……

什么是决策单调性?

在满足决策单调性的情况下,通常决策点会形如1111112222224444445555588888.....

即不可能会出现后面点的决策点小于前面点的决策点这种情况。

那么这个性质应该如何使用呢?

 1,二分。

  考虑到决策点单调递增,因此我们考虑用单调队列存下当前的决策选取情况。

  单调队列中存的量会带3个信息:这是哪个决策点,这个决策点会给哪个区间的点产生贡献(这是一个区间,所以算2个信息)

  相当于队列中存了很多个区间,假设当前的决策点是这样的:1111112222222333333,

  现在插入4这个决策,那么我们就是要找到最靠左的合法位置将决策序列变为类似这样的序列:111111222222444444444,

  因为决策单调,所以要覆盖肯定是一整段一整段的覆盖,因此我们先判断4是否可以覆盖完3这个区间,只需要看3的左端点是否可以被替换即可。

  我们重复覆盖整个区间这个操作,直到有个区间无法被完整覆盖,或者已经到了不合法的位置(因为第x个点只能给区间[x + 1, n]内的点产生贡献)。

  如果这个区间无法被完整覆盖,那么我们就在这个区间内二分找到最靠左的点使得4可以替换掉这个区间内的数,然后修改管理这个区间的数的区间,把被覆盖的区间让给4.

  每次操作前弹掉已经没有用的决策点,于是可以实现O(1)转移。(例如当前队首的决策点可以更新[3, x-1]这个区间,但我们已经枚举到x了,所以这个决策点显然就没有什么用了)  

  以下是某个年代久远的一道决策单调性优化的代码。

 /*[NOI2009]诗人小G by ww3113306*/
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 100100
#define LL long long
#define LD long double
#define ac 101000
#define inf 1000000000000000000LL
int t, L, p, n;
int Next[AC], s[AC], last[AC], l[AC], r[AC];//对应决策的管理区间,Next对last进行相反操作,以便输出
int q[AC], head, tail;//存下当前是哪个决策
LD f[AC];
LL sum[AC];
char ss[ac][]; inline LD qpow(LD x)//error!!!x也要用LD!!!
{
LD ans = ;int have = p;
while(have)
{
if(have & ) ans *= x;
x *= x, have >>= ;
}
return ans;
} inline LD count(int x, int j){return f[j] + qpow(abs(sum[x] - sum[j] - L - ));}//j --- > x inline void pre()
{
scanf("%d%d%d", &n, &L, &p);
for(R i = ; i <= n; i ++)
{
scanf("%s", ss[i] + );
s[i] = strlen(ss[i] + ) + ;//加上后面的空格
sum[i] = sum[i-] + s[i];//求出前缀和
}
} void half(int x)//二分查找
{
int now = q[tail], ll = max(l[now], x + ), rr = n, mid;//因为可能可以覆盖多个区间
while(ll < rr)
{
mid = (ll + rr) >> ;
if(count(mid, x) < count(mid, now)) rr = mid;//如果更优就往左缩短
else ll = mid + ;//不然就向右寻找
}
r[q[tail]] = ll - ;
q[++tail] = x, l[x] = ll, r[x] = n;
} inline void getans()
{
head = , tail = , q[] = , l[] = , r[] = n;
for(R i = ; i <= n; i ++)
{
while(r[q[head]] < i) ++head;//如果当前队首已经取不到了
int now = q[head];
f[i] = count(i, now);//error ??? 用函数的话会爆了会自动转换为inf?
last[i] = now;
if(count(n, q[tail]) < count(n, i)) continue;//如果最后一个都不够优,那就不二分了
while(count(l[q[tail]], q[tail]) > count(l[q[tail]], i)) --tail;//如果当前可以覆盖前面的整个区间
half(i);//注意上面的while要在调用half之前修改,这样取到的now才是正确的
}
} inline void write()
{
if(f[n] > inf) puts("Too hard to arrange");
else
{
printf("%lld\n", (LL)(f[n] + 0.5));//注意精度误差
for(R i = n; i; i = last[i]) Next[last[i]] = i;
int now = ;
for(R i = ; i <= n; i ++)
{
now = Next[now];//now先跳了吧
int be = now;//先只到这行结尾,因为for还要加的
for(R j = i; j < be; j ++) printf("%s ", ss[j] + );
printf("%s\n", ss[be] + ), i = be;//最后再赋i,因为for中还要用到当前i
}
}
puts("--------------------");
} int main()
{
scanf("%d", &t);
while(t--) pre(), getans(), write();
return ;
}

 2,分治

  假设我们当前的被决策区间是[l, r], 决策点区间是[ll, rr],那么我们取被决策区间的中点mid = (l + r) >> 1,然后在[ll, rr]中暴力寻找mid的最优决策点k,于是根据决策单调性,我们有:

  被决策区间[l, mid - 1]对应的决策点区间是[ll, k].同理,被决策区间[mid + 1, r]对应的决策点区间是[k, rr],于是我们就将这个区间划分为了2半,不断向下递归减小决策点范围即可用正确的复杂度求出所有的转移。

算法学习——决策单调性优化DP的更多相关文章

  1. Lightning Conductor 洛谷P3515 决策单调性优化DP

    遇见的第一道决策单调性优化DP,虽然看了题解,但是新技能√,很开森. 先%FlashHu大佬,反正我是看了他的题解和精美的配图才明白的,%%%巨佬. 废话不多说,看题: 题目大意 已知一个长度为n的序 ...

  2. CF868F Yet Another Minimization Problem 分治决策单调性优化DP

    题意: 给定一个序列,你要将其分为k段,总的代价为每段的权值之和,求最小代价. 定义一段序列的权值为$\sum_{i = 1}^{n}{\binom{cnt_{i}}{2}}$,其中$cnt_{i}$ ...

  3. 2018.09.28 bzoj1563: [NOI2009]诗人小G(决策单调性优化dp)

    传送门 决策单调性优化dp板子题. 感觉队列的写法比栈好写. 所谓决策单调性优化就是每次状态转移的决策都是在向前单调递增的. 所以我们用一个记录三元组(l,r,id)(l,r,id)(l,r,id)的 ...

  4. [BZOJ4850][JSOI2016]灯塔(分块/决策单调性优化DP)

    第一种方法是决策单调性优化DP. 决策单调性是指,设i>j,若在某个位置x(x>i)上,决策i比决策j优,那么在x以后的位置上i都一定比j优. 根号函数是一个典型的具有决策单调性的函数,由 ...

  5. BZOJ2216 Poi2011 Lightning Conductor 【决策单调性优化DP】

    Description 已知一个长度为n的序列a1,a2,...,an. 对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt( ...

  6. 决策单调性优化dp 专题练习

    决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队 ...

  7. BZOJ4899: 记忆的轮廓【概率期望DP】【决策单调性优化DP】

    Description 通往贤者之塔的路上,有许多的危机. 我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增, 在[1,n]中,一共有n个节点.我 ...

  8. 2018.10.14 NOIP训练 猜数游戏(决策单调性优化dp)

    传送门 一道神奇的dp题. 这题的决策单调性优化跟普通的不同. 首先发现这道题只跟r−lr-lr−l有关. 然后定义状态f[i][j]f[i][j]f[i][j]表示猜范围为[L,L+i−1][L,L ...

  9. 洛谷 P5897 - [IOI2013]wombats(决策单调性优化 dp+线段树分块)

    题面传送门 首先注意到这次行数与列数不同阶,列数只有 \(200\),而行数高达 \(5000\),因此可以考虑以行为下标建线段树,线段树上每个区间 \([l,r]\) 开一个 \(200\times ...

随机推荐

  1. 重装系统之无法在驱动器0的分区1上安装windows

    在通过U盘或光盘安装win8/win8.1/win10 时,遇到无法安装的问题,提示“无法在驱动器0的分区1上安装windows”,格式化分区也不能解决,进而提示Windows无法安装到这个磁盘,选中 ...

  2. 在平衡树的海洋中畅游(一)——Treap

    记得有一天翔哥毒奶我们: 当你们已经在平衡树的海洋中畅游时,我还在线段树的泥沼中挣扎. 我觉得其实像我这种对平衡树一无所知的蒟蒻也要开一开数据结构了. 然后花了一天啃了下最简单的平衡树Treap,感觉 ...

  3. Unity3d之树木创建的参数设定

    Unity3d之树木创建的参数设定 通常Unity3d创建树木经常会创建出很多奇葩的种类=_=,以下是创建出比较正常树木的基本参数 1:> 基本树干形状建立: 选择根建立分枝干设置分支干Di ...

  4. 浅谈js拖拽

    本文来自网易云社区 作者:刘凌阳 前言 本文依据半年前本人的分享<浅谈js拖拽>撰写,算是一篇迟到的文章. 基本思路 虽然现在关于拖拽的组件库到处都是,HTML5也把拖放纳入了标准.但考虑 ...

  5. Webpack 2 视频教程 002 - NodeJS 安装与配置

    原文发表于我的技术博客 这是我免费发布的高质量超清「Webpack 2 视频教程」. Webpack 作为目前前端开发必备的框架,Webpack 发布了 2.0 版本,此视频就是基于 2.0 的版本讲 ...

  6. ARM-GPIO

    操作GPIO有三种方法: 调用库函数读取IO的输入电平:uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef*GPIOx,uint16_t GPIO_pin): 操作寄 ...

  7. Redis常用操作-------Key(键)

    1.DEL key [key ...] 删除给定的一个或多个 key . 不存在的 key 会被忽略. 可用版本: >= 1.0.0 时间复杂度: O(N), N 为被删除的 key 的数量. ...

  8. SQLServer 中发布与订阅

    在对数据库做迁移的时候,会有很多方法,用存储过程,job,也可以用开源工具kettle,那么今天这些天变接触到了一种新的方法,就是SqlServer中自带的发布与订阅. 首先说明一下数据复制的流程.如 ...

  9. 修改docker的地址为阿里云源

    https://blog.csdn.net/jacabe/article/details/78575316

  10. 个人作业Week2-代码复审(修改明确了要求)

    代码复审 零,说在前面的话 大家完成了个人项目之后,都写了很多代码. 这些代码可能: 大括号换行/不换行 使用tab缩进/使用空格缩进 变量名函数名的定义很好/不好 每个函数都有详细的注释解释函数的功 ...