Problem

Description

小 \(G\) 是一个出色的诗人,经常作诗自娱自乐。但是,他一直被一件事情所困扰,那就是诗的排版问题。

一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的。小 \(G\) 给每首诗定义了一个行标准长度(行的长度为一行中符号的总个数),他希望排版后每行的长度都和行标准长度相差不远。显然排版时,不应改变原有的句子顺序,并且小 \(G\) 不允许把一个句子分在两行或者更多的行内。在满足上面两个条件的情况下,小 \(G\) 对于排版中的每行定义了一个不协调度, 为这行的实际长度与行标准长度差值绝对值的 \(P\) 次方,而一个排版的不协调度为所有行不协调度的总和。

小 \(G\) 最近又作了几首诗,现在请你对这首诗进行排版,使得排版后的诗尽量协调(即不协调度尽量小),并把排版的结果告诉他。

Input Format

输入文件中的第一行为一个整数 \(T\),表示诗的数量。

接下来为 \(T\) 首诗,这里一首诗即为一组测试数据。每组测试数据中的第一行为三个由空格分隔的正整数 \(N\),\(L\),\(P\),其中:\(N\) 表示这首诗句子的数目,\(L\) 表示这首诗的行标准长度,\(P\) 的含义见问题描述。

从第二行开始,每行为一个句子,句子由英文字母、数字、标点符号等符号组成(ASCII码33~127,但不包含'-')。

Output Format

于每组测试数据,若最小的不协调度不超过 \(10^{18}\) ,则第一行为一个数,表示不协调度。

\(Luogu\) 额外快乐:接下来若干行,表示你排版之后的诗。注意:在同一行的相邻两个句子之间需要用一个空格分开。如果有多个可行解,它们的不协调度都是最小值,则输出任意一个解均可。

若最小的不协调度超过 \(10^{18}\) ,则输出“\(Too\ hard\ to\ arrange\)”(不含引号)。每组测试数据结束后输出“--------------------”(不含引号),共20个“-”,“-”的ASCII码为45,请勿输出多余的空行或者空格。

Sample

Input

4
4 9 3
brysj,
hhrhl.
yqqlm,
gsycl.
4 9 2
brysj,
hhrhl.
yqqlm,
gsycl.
1 1005 6
poet
1 1004 6
poet

Output

108
brysj,
hhrhl.
yqqlm,
gsycl.
--------------------
32
brysj, hhrhl.
yqqlm, gsycl.
--------------------
Too hard to arrange
--------------------
1000000000000000000
poet
--------------------

Explanation

Explanatoin for Input

前两组输入数据中每行的实际长度均为6,后两组输入数据每行的实际长度均为4。一个排版方案中每行相邻两个句子之间的空格也算在这行的长度中(可参见样例中第二组数据)。每行末尾没有空格。

Range

测试点 \(T\) \(N\) \(L\) \(P\)
\(1\) \(\le 10\) \(\le18\) \(\le100\) \(\le5\)
\(2\) \(\le10\) \(\le2000\) \(\le60000\) \(\le10\)
\(3\) \(\le10\) \(\le2000\) \(\le60000\) \(\le10\)
\(4\) \(\le5\) \(\le100000\) \(\le200\) \(\le10\)
\(5\) \(\le5\) \(\le100000\) \(\le200\) \(\le10\)
\(6\) \(\le5\) \(\le100000\) \(\le3000000\) \(2\)
\(7\) \(\le5\) \(\le100000\) \(\le3000000\) \(2\)
\(8\) \(\le5\) \(\le100000\) \(\le3000000\) \(\le10\)
\(9\) \(\le5\) \(\le100000\) \(\le3000000\) \(\le10\)
\(10\) \(\le5\) \(\le100000\) \(\le3000000\) \(\le10\)

所有句子的长度不超过 \(30\) 。

Algorithm

\(DP\),决策单调性。

Mentality

我们发现数据范围不小不大,正好是 \(nlog\) 到 \(nlog^2\) 级别的。

先记录一个前缀和 \(q\) ,\(q[i]\) 表示句子 \(1-i\) 的长度总和,注意加上空格的长度。

首先观察到一个很显然的一维 \(dp\) ,\(f[i]\) 代表选到了第 \(i\) 个句子并且在此换行的最小不协调度,设 \(w(i,j)=abs(q[i]-q[j]-L-1)^P\) ,那么我们可以得到这样一个 \(n^2\) 的 \(dp\) :

\[f[i]=Min_{j<i}(f[j]+w(i,j))
\]

这确确实实是很简单的,但是很显然,复杂度过不了关。

那么考虑如何优化:

众所周知,\(dp\) 分为三个部分,枚举状态×枚举决策点×状态转移=时间复杂度。

我们考虑一一下手。

对于枚举状态的部分,由于必须顺着推过去,所以 \(O(n)\) 还是跑不了。

对于枚举决策点的部分,我们发现这个式子异常眼熟,一看就符合决策单调性的应用式。

对于状态转移的部分,\(O(1)\) 不能再优化了。

那么我们考虑如何利用决策单调性来优化这道题目。首先,对于一维 \(dp\) ,设 \(g[i]\) 为状态 \(i\) 的最优决策点,它的决策单调性的显著特征自然是:\(g[i-1]\le g[i]\) ,那么我们考虑利用这种决策单调性来做题。

我们利用队列记录下每个决策点 \(que\) ,并记录其对应下一个决策点左区间 \(L\) 。之所以这样做,是因为对于每个决策点,它对于每个区间的优劣性是不同的,所以它必定只会对一个区间最优。

但是这题之所以满足决策单调性,就是因为对于决策点而言,决策点位置的递增也意味着对应区间的递增,也就是说对于最右边的一段区间某个位置至位置 \(n\) ,最优决策点必定是最新的决策点。

对于这一点,我们可以有如下证明:

  • 首先我们回到问题,我们已经选出了一些句子分好了行,在当前行我们还未换行。
  • 不考虑下一行,那么如果还有句子加上当前的句子的长度小于标准长度,则一定要选,若长度之和大于标准长度,则只需要考虑选与不选。
  • 所以,对于当前行的决策,我们只会浮动在两个左右的决策点之间,因为 \(w\) 函数的指数性递增决定了我们选择的单调性。
  • 所以对于越往后的区间,它的最优决策点就越往后,因为 \(w\) 函数过大会造成 \(dp\) 的变劣。

那么我们的 \(dp\) 就分为了如下几个过程,设当前 \(dp[i]\) 正被更新:

  • 1、找到对应 \(i\) 的决策点区间,如果队首不符合就 \(head++\) ,直到当前队首决策点的对应区间包括 \(i\) 。
  • 2、\(f[i]=f[que[head]]+work(que[head],i)\) ,通过队首的决策点来转移。
  • 3、通过二分寻找出最左边的,以队尾决策点为决策点不如以 \(i\) 为决策点更优的位置,由于单调变优性,从这个位置往右的 \(dp\) 都满足以 \(i\) 为决策点是目前最优的。如果这个最左边的位置要小于队尾决策点对应的左区间,那么说明对于这个决策点对应的所有转移都不如 \(i\) 更优,所以弹出队尾,我们继续判断新的队尾与 \(i\) 的决策。
  • 4、当队尾的弹出停止的时候,我们二分出的位置往右的 \(dp\) 都以 \(i\) 决策最优,那么把当前队尾的决策区间右端点改为这个位置,\(q[++tail]=i\) 将 \(i\) 入队,且 \(i\) 的对应区间右端点为 \(n\) 。

不过需要注意,我们应用 \(long\ double\) 存下 \(dp\) 值,因为如果 \(dp\) 值大于 \(1e18\) 就不能用 \(long\ long\) 存了,但是用 \(long\ double\) 还是可以比较大小。科学计数法好。

完成!

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int head, tail, T, n, len, P, q[100001], g[100001], L[100001], que[100001];
char s[100002][31];
long double f[100001];
long double ksm(long double x) {
int base = P;
long double a = 1;
while (base) {
if (base & 1) a = a * x;
x = x * x;
base >>= 1;
}
return a;
} //快速幂
long double work(int i, int j) {
return f[j] + ksm((long double)abs(q[i] - q[j] - 1 - len));
} //计算以 j 为决策点 i 将得到的状态转移值
int find(int x, int y) {
int l = x, r = n + 1;
while (l < r) {
int mid = (l + r) >> 1;
work(mid, x) >= work(mid, y) ? r = mid : l = mid + 1;
}
return l;
} //查找 i 的最左最优决策位置
int main() {
cin >> T;
while (T--) {
cin >> n >> len >> P;
for (int i = 1; i <= n; i++) {
q[i] = 0;
scanf("%s", s[i]);
q[i] = strlen(s[i]) + q[i - 1] + 1; //单词长度前缀和
}
que[head = tail = 1] = 0; //初始化决策点队列
for (int i = 1; i <= n; i++) {
while (head < tail && L[head] <= i) //先找到对应区间包含 i 的位置,L
//记录的是下一个决策区间的左端点
head++;
g[i] = que[head];
f[i] = work(i, g[i]); //更新 dp 值
while (head < tail && L[tail - 1] >= find(que[tail], i)) //进行队尾判断
tail--;
L[tail] = find(que[tail], i); //更新队尾区间
que[++tail] = i; //决策点入队
}
if (f[n] > 1e18)
puts("Too hard to arrange");
else {
printf("%lld\n", (long long)f[n]);
int top = 0;
for (int i = n; i; i = g[i]) q[++top] = g[i];
q[0] = n;
while (top--) {
int L = q[top + 1] + 1, R = q[top];
for (int i = L; i < R; i++) printf("%s ", s[i]);
puts(s[R]); //行末不能有空格
}
}
puts("--------------------");
}
cout << endl;
}

【NOI 2009】诗人小G的更多相关文章

  1. [BZOJ 1563] [NOI 2009] 诗人小G(决策单调性)

    [BZOJ 1563] [NOI 2009] 诗人小G(决策单调性) 题面 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的.小 G ...

  2. NOI 2009 诗人小G

    题目描述 Description 小G是一个出色的诗人,经常作诗自娱自乐.但是,他一直被一件事情所困扰,那就是诗的排版问题. 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行 ...

  3. 解题:NOI 2009 诗人小G

    题面 今天考试考了,于是开始糊学决策单调性DP 这是一个完全不会优化DP的人 决策单调性DP的一种优化方法是用单调队列优化 存下{左端点l,右端点r,最优决策点p}的三元组,按照单调队列的通常操作来说 ...

  4. NOI 2009A 诗人小G

    NOI 2009A 诗人小G 诗人小G [问题描述] 小G是一个出色的诗人,经常作诗自娱自乐.但是,他一直被一件事情所困扰,那就是诗的排版问题. 一首诗包含了若干个句子,对于一些连续的短句,可以将它们 ...

  5. C++之路进阶——codevs2933(诗人小G)

    2933 诗人小G 2009年NOI全国竞赛  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 大师 Master     题目描述 Description 小G是一个出色的诗人 ...

  6. 【Luogu1912】【NOI2009】诗人小G(动态规划)

    [Luogu1912][NOI2009]诗人小G(动态规划) 题面 洛谷 题解 原来\(NOI\)这么多神仙题... 考虑一个极其明显的\(dp\) 设\(f[i]\)表示前\(i\)个句子产生的最小 ...

  7. LG1912 [NOI2009]诗人小G

    题意 题目描述 小G是一个出色的诗人,经常作诗自娱自乐.但是,他一直被一件事情所困扰,那就是诗的排版问题. 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以 ...

  8. bzoj1563: [NOI2009]诗人小G 决策单调性(1D1D)

    目录 题目链接 题解 代码 题目链接 bzoj1563: [NOI2009]诗人小G 题解 \(n^2\) 的dp长这样 \(f_i = min(f_j + (sum_i - sum_j - 1 - ...

  9. 1563: [NOI2009]诗人小G

    1563: [NOI2009]诗人小G https://lydsy.com/JudgeOnline/problem.php?id=1563 分析: 直接转移f[i]=f[j]+cost(i,j),co ...

  10. BZOJ1563/洛谷P1912 诗人小G 【四边形不等式优化dp】

    题目链接 洛谷P1912[原题,需输出方案] BZOJ1563[无SPJ,只需输出结果] 题解 四边形不等式 什么是四边形不等式? 一个定义域在整数上的函数\(val(i,j)\),满足对\(\for ...

随机推荐

  1. Presto 学习和使用笔记

    1.presto 表连接查询的连接条件中不支持使用函数 比如下面的脚本在presto中运行会报错 select t1.period_start_date, t2.statistic_date from ...

  2. HashMap 和 ConcurrentHashMap比较

    基础知识: 1. ConcurrentHashMap: (JDK1.7) segment数组,分段锁:segment 内部是 HashEnty数组,类似HashMap: 统计长度的方法,先不加锁统计两 ...

  3. 移植并修改成功的模拟iic读写EEPROM at24c02

    2010-04-24 12:58:00 注:如果要读24c128或264,地址位为16位的.现在的地址位为8位. protues仿真图 源程序如下: #include <iom16v.h> ...

  4. 大数据自学2-Hue集成环境中使用Sqoop组件从Sql Server导数据到Hive/HDFS

    安装完CDH后,发现里面的东东实在是太多了,对于一个初学大数据的来说就犹如刘姥姥进了大观园,很新奇,这些东东每个单拿出来都够喝一壶的. 接来来就是一步一步地学习了,先大致学习了每个模组大致做什么用的, ...

  5. css技巧-案例

    点击进入:http://herry.wuhairui.cn/cssSkill/main.html

  6. rsync 同步文件如何指定属主属组和权限

    rsync daemon 同步数据时,若想同时指定权限和属主属组,可以做如下配置: [test1] uid = admin gid = admin incoming chmod = Dug=rwx,D ...

  7. redis 缓存锁的实现方法

    1. redis加锁分类 redis能用的的加锁命令分表是INCR.SETNX.SET 2. 第一种锁命令INCR 这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执 ...

  8. 一键PHP/JAVA安装工具

    OneinStack是一键PHP/JAVA安装脚本工具,包含lnmp,lamp,lnmpa,ltmp,lnmh,MySQL,PostgreSQL,MongoDB等 建议使用 PHP7.1+MYSQL5 ...

  9. curl 异步捉取数据类

    <?php class RequestLib { /** * GET 请求 * @param string $url */ public static function http_get($ur ...

  10. 导入maven项目各个注解均报错了

    所遇问题: 导入maven项目各个注解均报错了; 思考1: 这个项目使用了springboot;spring是个”大容器”,所有对象的创建和管理都交给了它, (SpringBoot是一个框架,一种全新 ...