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\) 所能取到的最大值。

于是可以有这么一个转移方程:

\[f_{i,j} = \max_{k=1}^{n-1}(f_{i-1,k} + A_{\min(j, k)} - A_{\max(j,k) + 1})
\]

\(k\) 上界为 \(n - 1\),这是题面中要求了的。

包括 \(j\) 其实也 \(\in [1, n-1]\)

所以就有一个 \(O(n^3)\) 的写法了。

但是很明显,必须优化到 \(O(n^2)\) 才能过。

我们把 \(\min \max\) 拆开:

\[\begin{aligned}
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\),设

\[id_1 = \min(ID_{i}, ID_{i + 1}), id_2 = \min(ID_{i}, ID_{i + 1})
\]

于是有 \(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)\),分别表示从上面转移,从右侧转移,从左侧转移。

或者可以说是向下走,向右走,向左走转移(代码中的意义)。

于是有如下方程:

\[\begin{aligned}
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 题解的更多相关文章

  1. COCI2014/2015 Contest#1 D MAFIJA【基环树最大独立点集】

    T1725 天黑请闭眼 Online Judge:COCI2014/2015 Contest#1 D MAFIJA(原题) Label:基环树,断环+树形Dp,贪心+拓扑 题目描述 最近天黑请闭眼在 ...

  2. SCOI 2015 Day2 简要题解

    「SCOI2015」小凸玩密室 题意 小凸和小方相约玩密室逃脱,这个密室是一棵有 $ n $ 个节点的完全二叉树,每个节点有一个灯泡.点亮所有灯泡即可逃出密室.每个灯泡有个权值 $ A_i $,每条边 ...

  3. SCOI 2015 Day1 简要题解

    「SCOI2015」小凸玩矩阵 题意 一个 \(N \times M\)( $ N \leq M $ )的矩阵 $ A $,要求小凸从其中选出 $ N $ 个数,其中任意两个数字不能在同一行或同一列, ...

  4. 2018-2-6考试(COCI2014/2015 Contest#5)

    T1:FUNGHI(1s,32M,50pts)得分:50 题意:给你8个数组成一个环,要你求出其中连续的4个数,让它们的和最大 题解:暴力求出每一连续4个数之和,比较一下就好 标签:模拟 C++ Co ...

  5. Boston Key Party 2015 Heath Street 题解(Writeup)

    Heath Street是Boston Key Party 2015的一道数字取证题目,我们得到了一个叫做“secretArchive.6303dd5dbddb15ca9c4307d0291f77f4 ...

  6. CHD 2015迎新杯题解

    A.预防流感的拉面女神 简析:计算 n 的二进制表示里面 1 的个数 #include <cstdio> #include <cstring> #include <alg ...

  7. BZOJ4104:[Thu Summer Camp 2015]解密运算——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4104 对于一个长度为N的字符串,我们在字符串的末尾添加一个特殊的字符".".之 ...

  8. C++算法代码——求数列[coci2014/2015 contest #1]

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1815 题目描述 Mirko在数学课上以一种有趣的方式操作数列,首先,他写下一个数列A ...

  9. [Bzoj3743][Coci2015] Kamp【换根Dp】

    Online Judge:Bzoj3743 Label:换根Dp,维护最长/次长链 题目描述 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的 ...

  10. BZOJ4085: [Sdoi2015]音质检测

    BZOJ4085: [Sdoi2015]音质检测 由于这题太毒了,导致可能会被某些人卡评测,于是成了一道权限题... 本蒟蒻表示没钱氪金... 但是可以去洛谷/Vijos搞搞事... 但是洛谷上只能评 ...

随机推荐

  1. 剑指offer53(Java)-在排序数组中查找数字(简单)

    题目: 统计一个数字在排序数组中出现的次数. 示例 1: 输入: nums = [5,7,7,8,8,10], target = 8输出: 2示例 2: 输入: nums = [5,7,7,8,8,1 ...

  2. 力扣303(java)-区域和检索-数组不可变(简单)

    题目: 给定一个整数数组  nums,处理以下类型的多个查询: 计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的 和 ,其中 left <= rig ...

  3. EventBridge 事件总线及 EDA 架构解析

    ​简介:EventBridge 是事件驱动的具体落地产品,也是 EDA 的最佳实践方式. 作者:肯梦 作为 Gartner 定义的 10 大战略技术趋势之一,事件驱动架构(EDA)逐渐成为主流技术架构 ...

  4. 开课啦 dubbo-go 微服务升级实战

    简介: 杭州开课啦教育科技有限公司是一家致力于为中小学生提供学习辅导的在线教育公司,目前公司后端服务基础设施主要依托于阿里云原生,其中包含计算.网络.存储以及 Kubernetes 服务. 技术选型背 ...

  5. 那些你不知道的TCP冷门知识!

    简介: 最近在做数据库相关的事情,碰到了很多TCP相关的问题,新的场景新的挑战,有很多之前并没有掌握透彻的点,大大开了一把眼界,选了几个案例分享一下. 最近在做数据库相关的事情,碰到了很多TCP相关的 ...

  6. Javascript 机器学习的四个层次

    ​简介: Atwood定律说,凡是可以用Javascript实现的应用,最终都会用Javascript实现掉.作为最热门的机器学习领域,服务端是Python的主场,但是到了手机端呢?Android和i ...

  7. [ELK] Docker 运行 Elastic Stack 支持 TLS 的两种简单方式

    第一种就是 按照官方文档进行配置,指定证书位置开启. Run the Elastic Stack in Docker with TLS enabled. 第二种就是 9200 端口只暴露给本机,127 ...

  8. WPF 基于 Azure 的认知服务 情绪分析 语言检测 关键短语提取

    本文主要是来安利大家基于 Azure 的认知服务,主要是文本认知服务,可以做到分析输入文本的情绪,以及判断当前输入文本所属语言等功能 本文分为两个部分 ,一个就是在 Azure 上的配置,另一个就是 ...

  9. Ubuntu WSL 下编译并使用OpenJDK12

    一,安装Ubuntu WSL 1.Windows中设置WSL并安装Ubuntu wsl "控制面板"-->"程序"-->"启用或关闭Win ...

  10. 1.权限控制RBAC

    官方参考地址:https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/#privilege-escalation-prev ...