题目链接

洛谷P1912【原题,需输出方案】

BZOJ1563【无SPJ,只需输出结果】

题解

四边形不等式

什么是四边形不等式?

一个定义域在整数上的函数\(val(i,j)\),满足对\(\forall a \le b \le c \le d\)有

\[val(a,d) + val(b,c) \ge val(a,c) + val(b,d)
\]

那么我们称函数\(val(i,j)\)满足四边形不等式

一般地,当我们需要证明一个函数\(val(i,j)\)满足四边形不等式时,只需证对于\(\forall j < i\)有

\[val(j,i + 1) + val(j + 1,i) \ge val(j,i) + val(j + 1,i + 1)
\]

因为若该条件满足,

那么有

对于\(j < i\)有

\[val(j,i + 1) + val(j + 1,i) \ge val(j,i) + val(j + 1,i + 1)
\]

对于\(j + 1 < i\)有

\[val(j + 1,i + 1) + val(j + 2,i) \ge val(j + 1,i) + val(j + 2,i + 1)
\]

两式相加

\[val(j,i + 1) + val(j + 2,i) \ge val(j,i) + val(j + 2,i + 1)
\]

同理,只要满足第一个条件,对于\(\forall j + x < i + y\)都能推出

\[val(j,i + y) + val(j + x,i) \ge val(j,i) + val(j + x,i + y)
\]

所以对\(\forall a \le b \le c \le d\)有

\[val(a,d) + val(b,c) \ge val(a,c) + val(b,d)
\]

证毕

如何使用四边形不等式优化\(dp\)?

如果我们有这样一个递推式

\[f[i] = min\{f[j] + val(j,j)\}
\]

如果是\(1D1D\)方程,自然可以单调队列或者斜率优化

但是如果\(val(i,j)\)比较复杂,无法展开,我们不能有效地分离\(i,j\)变量,斜率优化就失效了

这个时候就要考虑\(val(j,i)\)是否满足四边形不等式

假使\(val(j,i)\)是满足的

那么我们对\(f[i]\)求出了一个最优转移位置,即为\(p[i]\)

那么对于\(\forall j < p[i]\),都有

\[f[p[i]] + val(p[i],i) \le f[j] + val(j,i)
\]

我们现在要求\(f[k]\),其中\(k > i\)

以上我们有\(j < p[i] < i < k\)

那么由四边形不等式:

\[val(j,k) + val(p[i],i) \ge val(j,i) + val(p[i],k)
\]

交换一下

\[val(j,i) + val(p[i],k) \le val(j,k) + val(p[i],i)
\]

与\(f[p[i]] + val(p[i],i) \le f[j] + val(j,i)\)相加

\[f[p[i]] + val(p[i],k) \le f[j] + val(j,k)
\]

得证\(f[k]\)的决策\(p[k] \ge p[i]\)

换言之,\(f[i]\)的决策满足决策单调性

对于\(\forall i > j\)都有\(p[i] \ge p[j]\)

我们就可以从这里入手优化这个\(O(n^2)\)的转移

为求出\(f[i]\),我们只需求出\(p[i]\)数组

一开始令\(p[i] = 0\),即未开始转移前所有位置的最优决策为位置\(0\)

之后从小枚举\(i\)

枚举到\(i\)时,\(i\)处最优决策更新完毕,那么\(\forall j \le i\),\(p[j]\)和\(f[j]\)都已经计算

为了更新\(p[i]\)数组,我们只需找到\(i\)所能更新的最左的位置\(l\),由于决策的单调性,\([l,n]\)的决策都将更新为\(i\)

此时二分判断即可

由于需要进行区间赋值,我们可以用一个队列来优化以上操作

具体地,储存若干三元组\((pos,l,r)\),表示区间\([l,r]\)的决策为\(pos\),每次更新从队尾逐个取出检查即可

复杂度优化为\(O(nlogn)\)

诗人小G

设\(f[i]\)为前\(i\)个句子排版的最小不和谐度

记\(len[i]\)为句子\(i\)的长度,\(sum[i]\)为句子长度前缀和

容易写出转移方程

\[f[i] = min\{f[j] + |(sum[i] - sum[j]) + (i - j - 1) - L|^{P}\}
\]

我们记\(val(j,i) = |(sum[i] - sum[j]) + (i - j - 1) - L|^{P}\),显然无法进行斜率优化

考虑\(val(j,i)\)是否满足四边形不等式,即\(f[i]\)的决策是否具有单调性

我们可以打表证明

要证\(val(j,i)\)满足四边形不等式,只需证对\(\forall j < i\)

\[val(j,i + 1) + val(j + 1,i) \ge val(j,i) + val(j + 1,i + 1)
\]

即证

\[val(j + 1,i) - val(j + 1,i + 1) \ge val(j,i) - val(j,i + 1)
\]

观察\(val(j,i) = |(sum[i] + i) - (sum[j] + j) - (L + 1)|^{P}\)

我们令\(x = (sum[i] + i) - (sum[j] + j) - (L + 1)\)

我们令\(y = (sum[i] + i) - (sum[j + 1] + j + 1) - (L + 1)\)

那么原式化为:

\[|y|^{P} - |y + len[i] + 1|^{P} \ge |x|^{P} - |x + len[i] + 1|^{P}
\]

又因为\(x > y\)

我们只需证对于函数\(f(x) = |x|^{P} - |x + c|^{P}\)关于\(x\)单调递减,其中\(c\)为大于\(0\)的常数

可以使用导数分类讨论得证

于是就可以用四边形不等式优化成\(O(nlogn)\)

不输出方案版:

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cmath>
  6. #include<map>
  7. #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
  8. #define REP(i,n) for (int i = 1; i <= (n); i++)
  9. #define mp(a,b) make_pair<int,int>(a,b)
  10. #define cls(s) memset(s,0,sizeof(s))
  11. #define cp pair<int,int>
  12. #define LL long double
  13. using namespace std;
  14. const int maxn = 100005,maxm = 100005,INF = 1000000000;
  15. inline int read(){
  16. int out = 0,flag = 1; char c = getchar();
  17. while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
  18. while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
  19. return out * flag;
  20. }
  21. inline void write(LL x){
  22. if (x / 10 >= 1) write(x / 10);
  23. putchar((int)floor(x - floor(x / 10) * 10) + '0');
  24. }
  25. int n,P,L;
  26. LL f[maxn],a[maxn],sum[maxn];
  27. char s[maxn][32];
  28. int head,tail;
  29. struct node{
  30. int j,l,r;
  31. }q[maxn * 3];
  32. LL qpow(LL a,int b){
  33. LL re = 1;
  34. for (; b; b >>= 1,a = a * a)
  35. if (b & 1) re = re * a;
  36. return re;
  37. }
  38. LL val(int i,int j){
  39. return qpow(fabs(sum[i] - sum[j] + (i - j - 1) - L),P);
  40. }
  41. bool check(int a,int b,int i){
  42. return f[a] + val(i,a) <= f[b] + val(i,b);
  43. }
  44. int main(){
  45. int T = read();
  46. while (T--){
  47. n = read(); L = read(); P = read();
  48. sum[0] = f[0] = 0;
  49. REP(i,n){
  50. scanf("%s",s[i] + 1),a[i] = strlen(s[i] + 1);
  51. sum[i] = sum[i - 1] + a[i];
  52. f[i] = val(i,0);
  53. }
  54. q[head = tail = 0] = (node){0,1,n};
  55. node u; int l,r,mid;
  56. for (int i = 1; i <= n; i++){
  57. f[i] = f[q[head].j] + val(i,q[head].j);
  58. q[head].l++;
  59. if (q[head].l > q[head].r) head++;
  60. while (head <= tail){
  61. u = q[tail--];
  62. if (check(i,u.j,u.l)){
  63. if (head > tail){
  64. q[++tail] = (node){i,i + 1,n};
  65. break;
  66. }
  67. }
  68. else if (!check(i,u.j,u.r)){
  69. q[++tail] = u;
  70. if (u.r == n) break;
  71. else{
  72. q[++tail] = (node){i,u.r + 1,n};
  73. break;
  74. }
  75. }
  76. else {
  77. l = u.l; r = u.r;
  78. while (l < r){
  79. mid = l + r >> 1;
  80. if (check(i,u.j,mid)) r = mid;
  81. else l = mid + 1;
  82. }
  83. q[++tail] = (node){u.j,u.l,l - 1};
  84. q[++tail] = (node){i,l,n};
  85. break;
  86. }
  87. }
  88. }
  89. if (f[n] > qpow(10,18)) puts("Too hard to arrange");
  90. else write(f[n]),puts("");
  91. printf("--------------------");
  92. if (T) puts("");
  93. }
  94. return 0;
  95. }

输出方案版

  1. #include<algorithm>
  2. #include<iostream>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cmath>
  6. #include<map>
  7. #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
  8. #define REP(i,n) for (int i = 1; i <= (n); i++)
  9. #define mp(a,b) make_pair<int,int>(a,b)
  10. #define cls(s) memset(s,0,sizeof(s))
  11. #define cp pair<int,int>
  12. #define LL long double
  13. using namespace std;
  14. const int maxn = 100005,maxm = 100005,INF = 1000000000;
  15. inline int read(){
  16. int out = 0,flag = 1; char c = getchar();
  17. while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
  18. while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
  19. return out * flag;
  20. }
  21. inline void write(LL x){
  22. if (x / 10 >= 1) write(x / 10);
  23. putchar((int)floor(x - floor(x / 10) * 10) + '0');
  24. }
  25. int n,P,L,p[maxn];
  26. LL f[maxn],a[maxn],sum[maxn];
  27. char s[maxn][32];
  28. int head,tail;
  29. struct node{
  30. int j,l,r;
  31. }q[maxn * 3];
  32. LL qpow(LL a,int b){
  33. LL re = 1;
  34. for (; b; b >>= 1,a = a * a)
  35. if (b & 1) re = re * a;
  36. return re;
  37. }
  38. LL val(int i,int j){
  39. return qpow(fabs(sum[i] - sum[j] + (i - j - 1) - L),P);
  40. }
  41. bool check(int a,int b,int i){
  42. return f[a] + val(i,a) <= f[b] + val(i,b);
  43. }
  44. void print(int u){
  45. if (p[u]) print(p[u]);
  46. for (int i = p[u] + 1; i <= u; i++){
  47. printf("%s",s[i] + 1);
  48. putchar(i < u ? ' ' : '\n');
  49. }
  50. }
  51. int main(){
  52. int T = read();
  53. while (T--){
  54. n = read(); L = read(); P = read();
  55. sum[0] = f[0] = 0;
  56. REP(i,n){
  57. scanf("%s",s[i] + 1),a[i] = strlen(s[i] + 1);
  58. sum[i] = sum[i - 1] + a[i];
  59. f[i] = val(i,0);
  60. }
  61. q[head = tail = 0] = (node){0,1,n};
  62. node u; int l,r,mid;
  63. for (int i = 1; i <= n; i++){
  64. f[i] = f[q[head].j] + val(i,q[head].j);
  65. p[i] = q[head].j;
  66. q[head].l++;
  67. if (q[head].l > q[head].r) head++;
  68. while (head <= tail){
  69. u = q[tail--];
  70. if (check(i,u.j,u.l)){
  71. if (head > tail){
  72. q[++tail] = (node){i,i + 1,n};
  73. break;
  74. }
  75. }
  76. else if (!check(i,u.j,u.r)){
  77. q[++tail] = u;
  78. if (u.r == n) break;
  79. else{
  80. q[++tail] = (node){i,u.r + 1,n};
  81. break;
  82. }
  83. }
  84. else {
  85. l = u.l; r = u.r;
  86. while (l < r){
  87. mid = l + r >> 1;
  88. if (check(i,u.j,mid)) r = mid;
  89. else l = mid + 1;
  90. }
  91. q[++tail] = (node){u.j,u.l,l - 1};
  92. q[++tail] = (node){i,l,n};
  93. break;
  94. }
  95. }
  96. }
  97. if (f[n] > qpow(10,18)) puts("Too hard to arrange");
  98. else{
  99. write(f[n]),puts("");
  100. print(n);
  101. }
  102. printf("--------------------");
  103. if (T) puts("");
  104. }
  105. return 0;
  106. }

BZOJ1563/洛谷P1912 诗人小G 【四边形不等式优化dp】的更多相关文章

  1. [BZOJ1563][NOI2009]诗人小G(决策单调性优化DP)

    模板题. 每个决策点都有一个作用区间,后来的决策点可能会比先前的优.于是对于每个决策点二分到它会比谁在什么时候更优,得到新的决策点集合与区间. #include<cstdio> #incl ...

  2. [NOI2009]诗人小G 决策单调性优化DP

    第一次写这种二分来优化决策单调性的问题.... 调了好久,,,各种细节问题 显然有DP方程: $f[i]=min(f[j] + qpow(abs(sum[i] - sum[j] - L - 1))); ...

  3. 洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP

    洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他. 玩具上有一个数列,数列中某些项的值可能会 ...

  4. CF321E Ciel and Gondolas Wqs二分 四边形不等式优化dp 决策单调性

    LINK:CF321E Ciel and Gondolas 很少遇到这么有意思的题目了.虽然很套路.. 容易想到dp \(f_{i,j}\)表示前i段分了j段的最小值 转移需要维护一个\(cost(i ...

  5. hdu 2829 Lawrence(四边形不等式优化dp)

    T. E. Lawrence was a controversial figure during World War I. He was a British officer who served in ...

  6. 【转】斜率优化DP和四边形不等式优化DP整理

    (自己的理解:首先考虑单调队列,不行时考虑斜率,再不行就考虑不等式什么的东西) 当dp的状态转移方程dp[i]的状态i需要从前面(0~i-1)个状态找出最优子决策做转移时 我们常常需要双重循环 (一重 ...

  7. codevs3002石子归并3(四边形不等式优化dp)

    3002 石子归并 3 参考 http://it.dgzx.net/drkt/oszt/zltk/yxlw/dongtai3.htm  时间限制: 1 s  空间限制: 256000 KB  题目等级 ...

  8. 洛谷 P3580 - [POI2014]ZAL-Freight(单调队列优化 dp)

    洛谷题面传送门 考虑一个平凡的 DP:我们设 \(dp_i\) 表示前 \(i\) 辆车一来一回所需的最小时间. 注意到我们每次肯定会让某一段连续的火车一趟过去又一趟回来,故转移可以枚举上一段结束位置 ...

  9. 学习笔记:四边形不等式优化 DP

    定义 & 等价形式 四边形不等式是定义在整数集上的二元函数 \(w(x, y)\). 定义:对于任意 \(a \le b \le c \le d\),满足交叉小于等于包含(即 \(w(a, c ...

随机推荐

  1. python基础数据类型补充

    python_day_7 一. 今日主要内容: 1. 补充基础数据类型的相关知识点 str. join() 把列表变成字符串 列表不能再循环的时候删除. 因为索引会跟着改变 字典也不能直接循环删除.把 ...

  2. WebGL树形结构的模型渲染流程

    今天和大家分享的是webgl渲染树形结构的流程.用过threejs,babylonjs的同学都知道,一个大模型都是由n个子模型拼装而成的,那么如何依次渲染子模型,以及渲染每个子模型在原生webgl中的 ...

  3. idea scala 报 with UTF-8 Please try specifying another one using the -encoding option

    现象如下图, 代码里有汉字,执行代码报错,说编码格式不对, 修改方式如上面,将右下角的编码格式修改成 u8即可.

  4. PLSQL变量和类型,流程控制语句,集合

    ---PLSQL 调试授权 GRANT debug any procedure, debug connect session TO scott; --定义变量 declare part_number ...

  5. IO多路复用(一)-- Select、Poll、Epoll

    在上一篇博文中提到了五种IO模型,关于这五种IO模型可以参考博文IO模型浅析-阻塞.非阻塞.IO复用.信号驱动.异步IO.同步IO,本篇主要介绍IO多路复用的使用和编程. IO多路复用的概念 多路复用 ...

  6. 【Python进阶】用 Python 统计字数

    问题描述: 用 Python 实现函数 count_words(),该函数输入字符串 s 和数字 n,返回 s 中 n 个出现频率最高的单词.返回值是一个元组列表,包含出现次数最高的 n 个单词及其次 ...

  7. Gradle快速上手——从Maven到Gradle

    [本文写作于2018年7月5日] 本文适合于有一定Maven应用基础,想快速上手Gradle的读者. 背景 Maven.Gradle都是著名的依赖管理及自动构建工具.提到依赖管理与自动构建,其重要性在 ...

  8. Python基础知识-09-函数

    python其他知识目录 1.函数介绍 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如pr ...

  9. 【转】SWFUpload 官方说明文档(2.5.0版)

    原文出自:http://www.runoob.com/w3cnote/swfupload-document.html SWFUpload使用指南请查阅:http://www.w3cschool.cc/ ...

  10. zend安装及破解

    Zend下载 https://pan.baidu.com/s/1fCfUQ0j7dxEtOzbNnsgODg 破解: 1.打开dmg文件安装,将Zend Studio拖拽至applications进行 ...