P7959 [COCI2014-2015#6] WTF 题解
P7959 [COCI2014-2015#6] WTF 题解
呃,是一道
DP题
说实话,原题实际上是不要输出一种方法的……但是似乎放这道题的人想增加一点难度?
这里有两种做法,但都是 DP。
预备观察
我们首先观察一些性质,以方便解题。
循环不变:我们可以观察到,在 \(n\) 次变换后,序列会还原。也就是说,两个循环在同一个 \(i\) 上操作的序列是一样的。
下标大小:其实可以看到,下标是一大一小,也就是 \(\min(ID_{i}, \mathit{ID}_{i+1})\) 和 \(\max(ID_{i}, ID_{i + 1})+1\)。意味着我们在 \(ID_{i}\) 的选择关于,且仅关于 \(ID_{i - 1}\) 的选择。所以考虑
DP转移。连续性:不难发现,其实选择是这么一些边:\((ID_{i}, ID_{i + 1})\) 和 \((ID_{i + 1}, ID_{i + 2})\),也就是说每一个状态是相关联的。
接下来就可以开始正式解题了。
感觉上面讲的都是废话
解法1:强行DP
这也是我拿到这一道题的第一想法……也是正解的一种吧
在观察出来下标大小的关系之后,其实就可以设一个 \(DP\) 了。
令 \(f_{i,j}\) 表示在 \(ID_{i + 1}\) 选 \(j\) 所能取到的最大值。
于是可以有这么一个转移方程:
\]
\(k\) 上界为 \(n - 1\),这是题面中要求了的。
包括 \(j\) 其实也 \(\in [1, n-1]\)
所以就有一个 \(O(n^3)\) 的写法了。
但是很明显,必须优化到 \(O(n^2)\) 才能过。
我们把 \(\min \max\) 拆开:
f_{i,j} = \max&( A_{j} + \max_{k = j}^{n - 1}(f_{i-1, k} - A_{k+1}), \\
&-A_{j+1} + \max_{k = 1}^{j}(f_{i-1,k} + A_{k}))
\end{aligned}
\]
其实内部关于 \(j\) 的边界并没有那么重要
很明显,后面两个部分可以通过前后缀 \(\max\) 搞定。于是我们可以在 \(O(1)\) 内转移。
总时间复杂度成功变为 \(O(n^2)\)。
不过还要注意一个点,每一次转移的时候,需要手动模拟一次 \(Rotate(n, r)\)。
那么核心代码如下:
pre[0] = suf[n] = -1e9;
for (i = 1; i <= n; ++i, rotate()) {
// prefix k
for (k = 1; k < n; ++k) {
// pre[k] = max(pre[k - 1], f[i - 1][k] + A[k]);
if (pre[k - 1] >= f[i - 1][k] + A[k]) {
pre[k] = pre[k - 1];
pref[k] = pref[k - 1];
} else {
pre[k] = f[i - 1][k] + A[k];
pref[k] = k;
}
}
// suffix k
for (k = n - 1; k; --k) {
// suf[k] = max(suf[k + 1], f[i - 1][k] - A[k + 1]);
if (suf[k + 1] >= f[i - 1][k] - A[k + 1]) {
suf[k] = suf[k + 1];
suff[k] = suff[k + 1];
} else {
suf[k] = f[i - 1][k] - A[k + 1];
suff[k] = k;
}
}
for (j = 1; j < n; ++j) { // enum cur ID[i + 1]
int p = pre[j] - A[j + 1], s = suf[j] + A[j];
if (p >= s) {
f[i][j] = p;
trans[i][j] = pref[j];
} else {
f[i][j] = s;
trans[i][j] = suff[j];
}
}
}
最后通过 trans 数组输出方案即可。
不过说实话,这个空间复杂度确实不够优秀。
做法2:std做法
其实可以发现,对于每一个 \(i\),设
\]
于是有 \(sum += A_{id_1} -A_{id_2 +1}\)。
这似乎提醒这我们做一个前缀差分。
于是我们设 \(b_{i} = A_{i + 1} - A_{i}\)。
所以可以得到 \(A_{id_2 + 1} - A_{id_1} = \sum_{i = id_1}^{id_2} b_{i}\)。
原本我们需要最大化,那么此时,我们需要最小化 \(A_{id_2 + 1} - A_{id_1}\)。
不过,如果我们把初始的 \(A\) 序列全部取反,那么我们还是需要最大化上面这个式子。
贴出的代码中也做了如上操作。
注意加减顺序。以及 \(b\) 只有 \(n-1\) 个元素。
于是我们可以构建出一个 \((n-1) \times n\) 的矩阵 \(B\),其中每一行是对应旋转后的 \(A\) 的差分序列。
我们在寻找 \(sum\) 的过程,其实就是把所有路径上的 \(b\) 加起来,于是,问题转化为寻找在 \(B\) 上的一条最短路径。
不过,由于我们只能向下,或者左右走,并且不能重复走,所以也考虑 \(DP\)。
设 \(f_{i, j, k}\) 表示,走到 \((i, j)\) 这个位置,来的方向是 \(k\) ,的最长路径。
\(k \in [0, 3)\),分别表示从上面转移,从右侧转移,从左侧转移。
或者可以说是向下走,向右走,向左走转移(代码中的意义)。
于是有如下方程:
f_{i, j, 0} &= \max(f_{i-1, j, 0/1/2}) + B_{i,j} \\
f_{i, j, 1} &= \max(f_{i, j+1, 0/1}) + B_{i, j} \\
f_{i, j, 2} &= \max(f_{i, j-1, 0/2}) + B_{i, j}
\end{aligned}
\]
记录一下转移来的路径,在拐点的地方输出即可。
为了偷懒,就直接贴出不记录路径的代码了。
总时间复杂度 \(O(n^2)\):
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 3003, MINUS_INF = -1e9;
int a[N][N];
int b[N][N];
int dp[N][N][3];
#define DOWN 0
#define LEFT 1
#define RIGHT 2
// 三个方向选其优
int best(int i, int j) {
return max(dp[i][j][DOWN],
max(dp[i][j][LEFT], dp[i][j][RIGHT]));
}
int main () {
int n, r;
cin >> n >> r;
// 注意整个程序的下标是从 0 开始
// 也就是 [0, n) 而非 [1, n]
for (int i = 0; i < n; ++i) {
cin >> a[0][i];
a[0][i] *= -1;
int position = i;
// 构建旋转后的序列
for (int j = 1; j < n; ++j) {
position = (position + r) % n;
a[j][position] = a[0][i];
}
}
// 初始化dp表
for (int i = 0; i < n; ++i)
for (int j = 0; j < n - 1; ++j) {
// 构建差分序列
b[i][j] = a[i][j + 1] - a[i][j];
for (int k = 0; k < 3; ++k)
dp[i][j][k] = MINUS_INF;
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n - 1; ++j) {
// 处理从上一行的转移
dp[i][j][DOWN] = b[i][j] + (i > 0 ? best(i - 1, j) : 0);
// 处理从左边转移
if (j > 0)
dp[i][j][RIGHT] = b[i][j] +
max(dp[i][j - 1][DOWN], dp[i][j - 1][RIGHT]);
}
// 反着来一次从右边的转移
for (int j = n - 3; j >= 0; --j)
dp[i][j][LEFT] = b[i][j] +
max(dp[i][j + 1][DOWN], dp[i][j + 1][LEFT]);
}
// 输出最终的答案
int sol = MINUS_INF;
for (int j = 0; j < n - 1; ++j)
sol = max(sol, best(n - 1, j));
cout << sol << endl;
}
P7959 [COCI2014-2015#6] WTF 题解的更多相关文章
- COCI2014/2015 Contest#1 D MAFIJA【基环树最大独立点集】
T1725 天黑请闭眼 Online Judge:COCI2014/2015 Contest#1 D MAFIJA(原题) Label:基环树,断环+树形Dp,贪心+拓扑 题目描述 最近天黑请闭眼在 ...
- SCOI 2015 Day2 简要题解
「SCOI2015」小凸玩密室 题意 小凸和小方相约玩密室逃脱,这个密室是一棵有 $ n $ 个节点的完全二叉树,每个节点有一个灯泡.点亮所有灯泡即可逃出密室.每个灯泡有个权值 $ A_i $,每条边 ...
- SCOI 2015 Day1 简要题解
「SCOI2015」小凸玩矩阵 题意 一个 \(N \times M\)( $ N \leq M $ )的矩阵 $ A $,要求小凸从其中选出 $ N $ 个数,其中任意两个数字不能在同一行或同一列, ...
- 2018-2-6考试(COCI2014/2015 Contest#5)
T1:FUNGHI(1s,32M,50pts)得分:50 题意:给你8个数组成一个环,要你求出其中连续的4个数,让它们的和最大 题解:暴力求出每一连续4个数之和,比较一下就好 标签:模拟 C++ Co ...
- Boston Key Party 2015 Heath Street 题解(Writeup)
Heath Street是Boston Key Party 2015的一道数字取证题目,我们得到了一个叫做“secretArchive.6303dd5dbddb15ca9c4307d0291f77f4 ...
- CHD 2015迎新杯题解
A.预防流感的拉面女神 简析:计算 n 的二进制表示里面 1 的个数 #include <cstdio> #include <cstring> #include <alg ...
- BZOJ4104:[Thu Summer Camp 2015]解密运算——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4104 对于一个长度为N的字符串,我们在字符串的末尾添加一个特殊的字符".".之 ...
- C++算法代码——求数列[coci2014/2015 contest #1]
题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1815 题目描述 Mirko在数学课上以一种有趣的方式操作数列,首先,他写下一个数列A ...
- [Bzoj3743][Coci2015] Kamp【换根Dp】
Online Judge:Bzoj3743 Label:换根Dp,维护最长/次长链 题目描述 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的 ...
- BZOJ4085: [Sdoi2015]音质检测
BZOJ4085: [Sdoi2015]音质检测 由于这题太毒了,导致可能会被某些人卡评测,于是成了一道权限题... 本蒟蒻表示没钱氪金... 但是可以去洛谷/Vijos搞搞事... 但是洛谷上只能评 ...
随机推荐
- 记一次 Go 调用系统命令出现的问题分析
首先在程序中封装了下面一个函数用来执行系统命令: // 执行系统命令 func executeCommand(command string, output, outerr io.Writer) err ...
- 力扣904(Java)-水果成篮(中等)
题目: 你正在探访一家农场,农场从左到右种植了一排果树.这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 . 你想要尽可能多地收集水果.然而,农场的主人 ...
- 力扣578(MySQL)-查询回答率最高的问题(中等)
题目: 从 survey_log 表中获得回答率最高的问题,survey_log 表包含这些列:id, action, question_id, answer_id, q_num, timestamp ...
- 力扣24(java&python)-两两交换链表中的节点(中等)
题目: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点.你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换) 示例 1: 输入:head = [1,2,3,4] 输出:[ ...
- ACK One 构建应用系统的两地三中心容灾方案
简介:本文侧重介绍了通过 ACK One 的多集群应用分发功能,可以帮助企业管理多集群环境,通过多集群主控示例提供的统一的应用下发入口,实现应用的多集群分发,差异化配置,工作流管理等分发策略.结合 ...
- Quick BI V4.0功能“炸弹”来袭,重磅推出即席分析、模板市场、企业微信免密登录等强势功能
简介: 2021年7月,Quick BI公共云版本迭代新功能:重磅推出即席分析.模板市场,分析门槛再降低:推出企业微信无缝对接,移动端类目个性配置及管理提升多端能力:数据建模配置交互升级至拖拽模式提升 ...
- 前端使用 Konva 实现可视化设计器(7)- 导入导出、上一步、下一步
请大家动动小手,给我一个免费的 Star 吧~ 这一章实现导入导出为JSON文件.另存为图片.上一步.下一步. github源码 gitee源码 示例地址 导出为JSON文件 提取需要导出的内容 ge ...
- git fatal detected dubious ownership in repository 的解决方法
我换了一台电脑,将旧电脑的硬盘换到新电脑上:我装了双系统,切换到另一个系统时:我发现了 git 代码仓库无法执行 git 命令,不断报错 fatal: detected dubious ownersh ...
- WPF 已知问题 InputEventArgs 的 Timestamp 属性是静态的导致事件之间相互影响
本文记录一个 WPF 已知的设计问题,当前此问题已经被大佬修复,这个设计问题刚好属于比较边缘的模块,我写了这么多年的代码还没有踩到这个坑一次,也没有听到有谁提到这个坑 远古时候,不知道大佬是故意还是失 ...
- CF633H (线段树维护矩乘 + 莫队)
Fibonacci-ish II 题意:给定一个长度最大为 \(30000\) 的序列,和最多 \(30000\) 个询问,每个询问问某区间 \([L,R]\) 里的数,去掉重复然后排序之后,依次乘上 ...