【NOI 2009】诗人小G
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\) :
\]
这确确实实是很简单的,但是很显然,复杂度过不了关。
那么考虑如何优化:
众所周知,\(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的更多相关文章
- [BZOJ 1563] [NOI 2009] 诗人小G(决策单调性)
[BZOJ 1563] [NOI 2009] 诗人小G(决策单调性) 题面 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的.小 G ...
- NOI 2009 诗人小G
题目描述 Description 小G是一个出色的诗人,经常作诗自娱自乐.但是,他一直被一件事情所困扰,那就是诗的排版问题. 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行 ...
- 解题:NOI 2009 诗人小G
题面 今天考试考了,于是开始糊学决策单调性DP 这是一个完全不会优化DP的人 决策单调性DP的一种优化方法是用单调队列优化 存下{左端点l,右端点r,最优决策点p}的三元组,按照单调队列的通常操作来说 ...
- NOI 2009A 诗人小G
NOI 2009A 诗人小G 诗人小G [问题描述] 小G是一个出色的诗人,经常作诗自娱自乐.但是,他一直被一件事情所困扰,那就是诗的排版问题. 一首诗包含了若干个句子,对于一些连续的短句,可以将它们 ...
- C++之路进阶——codevs2933(诗人小G)
2933 诗人小G 2009年NOI全国竞赛 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 小G是一个出色的诗人 ...
- 【Luogu1912】【NOI2009】诗人小G(动态规划)
[Luogu1912][NOI2009]诗人小G(动态规划) 题面 洛谷 题解 原来\(NOI\)这么多神仙题... 考虑一个极其明显的\(dp\) 设\(f[i]\)表示前\(i\)个句子产生的最小 ...
- LG1912 [NOI2009]诗人小G
题意 题目描述 小G是一个出色的诗人,经常作诗自娱自乐.但是,他一直被一件事情所困扰,那就是诗的排版问题. 一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以 ...
- bzoj1563: [NOI2009]诗人小G 决策单调性(1D1D)
目录 题目链接 题解 代码 题目链接 bzoj1563: [NOI2009]诗人小G 题解 \(n^2\) 的dp长这样 \(f_i = min(f_j + (sum_i - sum_j - 1 - ...
- 1563: [NOI2009]诗人小G
1563: [NOI2009]诗人小G https://lydsy.com/JudgeOnline/problem.php?id=1563 分析: 直接转移f[i]=f[j]+cost(i,j),co ...
- BZOJ1563/洛谷P1912 诗人小G 【四边形不等式优化dp】
题目链接 洛谷P1912[原题,需输出方案] BZOJ1563[无SPJ,只需输出结果] 题解 四边形不等式 什么是四边形不等式? 一个定义域在整数上的函数\(val(i,j)\),满足对\(\for ...
随机推荐
- 设计模式之Factory(工厂)(转)
定义:提供创建对象的接口. 为何使用? 工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见. 为什么工厂模式是如此常用?因为工厂模式就 ...
- 转:wcf大文件传输解决之道(2)
此篇文章主要是基于http协议应用于大文件传输中的应用,现在我们先解析下wcf中编码器的定义,编码器实现了类的编码,并负责将Message内存中消息转变为网络发送的字节流或者字节缓冲区(对于发送方而言 ...
- 转:C#判断ContextMenuStrip右键菜单的来源(从哪个控件弹出来的)
转载自:http://hi.baidu.com/cookiemulan/item/82df8ff867dd53cc531c26c7 有时候,为了提高性能和节约资源,我们会为多个控件,指定同一个右键弹出 ...
- android studio 添加get,set方法快捷方式
android studio 添加get,set方法快捷方式
- 新浪微博 [异常问题] 414 Request-URL Too Large
新浪微博 [异常问题] 414 Request-URL Too Large 浏览器上打开新浪微博,或则日志是返回结果提示:414 Request-URL Too Large原因:因同IP访问微博页面过 ...
- Numpy 基本除法运算和模运算
基本算术运算符+.-和*隐式关联着通用函数add.subtract和multiply 在数组的除法运算中涉及三个通用函数divide.true_divide和floor_division,以及两个对应 ...
- oj练习---dp专题
1.POJ 3744 Scout YYF I 经典的dp模型,但是要用到快速矩阵幂加速,分段的思想 # include <stdio.h> # include <algorithm& ...
- GoldenGate 12.3发布
新特性: oracle db1. 支持12.2 oracle db2. 支持微服务架构, 可以使用restful api 管理OGG3. Parallel replicat,性能比integrated ...
- Linux的远程连接工具:SSH的安装
在Linux执行命令很不方便,另外我们需要将自己计算机中的文件上传到Linux中,因此使用远程连接工具还是比较方便的. SSH安装 SSH的使用 打开安装好的软件:SSH Secure File Tr ...
- js 解密 16进制转10进制,再取ascii码的对应值
如:\x64 对应 16进制 0x64 转10进制就是 0x64.toString(10) == 100, 查对应的ascii码表得到 ‘d' <div id=code style='displ ...