// 标注:本文旨在为博主确立一种题解的基本范式,以避免博主的题解流于AC代码的粘贴。此基本范式为:完整而简洁明了的思路及其推导说明,力图触及问题的本质并衍生对同类问题的思路分析,使得题解具有泛用性,同时可以写出对代码的优化过程。

简单题,流水线问题的变形: \(U、D、C\)是三条流水线,每次步进都要转换到别的流水线上,其中 \(U[i]、D[i]、C[i]\) 就是转换到对应类型的流水线的第 \(i\) 个元素的成本,求最小总成本 \(^{①}\)


假设 \(f(i)\) 为对前 \(i\) 个元素构成的子问题的最优解集, \(f(i;U)\) 为以 \(U\) 类型为终点的前 \(i\) 个元素构成的子问题的最优解,\(f(i;D)\)、\(f(i; C)\) 同理。 \(^{②}\)

以 \(U[i], D[i], C[i]\) 表示第 \(i\) 个地铁站对应类型的建设成本

不妨设\(f(0;U) = f(0;D) = f(0;C) = 0\)

而且初始值 \(f(1;U) = U[1]\) 、 \(f(1;D) = D[1]\) 、 \(f(1;C) = C[1]\)


定义好状态转移方程的属性之后,结合题意(①)以及 \(f(i)\) 的意义(②),可得状态转移方程:

\[\begin{equation}
f(i) =
\left\{
\begin{array}{**lr**}
f(i;U) &=& min\{f(i-1;D), f(i-1; C)\} + U[i] \\
f(i;D) &=& min\{f(i-1;U), f(i-1; C)\} + D[i] \\
f(i;C) &=& min\{f(i-1;U), f(i-1; D)\} + C[i]
\end{array}
\right.
\ \ \ \ \ ,\ i = 1,2,3, \dots, n
\end{equation}
\]

简化就是: \(dp[i][type] = min\{dp[i-1][t] \text{ | t} \in (S-\{type\})\}+cost[i][type] \text{ , 其中 }type\in S = \{U,D,C\}\)


构造出状态转移方程,就可以写代码了。

代码流程:

  • 用Subway结构体存储每个地铁站的不同类型的建设成本,然后用一个subway数组存储所有地铁站的不同类型的建设成本
  • 然后遍历subway数组并用状态转移方程进行状态转移(记得将dp数组初始化为0,以避免上组数据的影响)
  • 最后dp[n][u], dp[n][d], dp[n][c]中最小值即为结果。

时间复杂度为 \(O(n)\)

空间复杂度为 \(O(n)\) , 具体大约是 \(7n\) ,开了两个大数组,7 * MAXN * sizeof(long long) 容易MLE(特别是对于long long 癌)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std; const int MAXN = 200005;
struct Subway{
LL U;
LL D;
LL C;
}subway[MAXN]; LL dp[MAXN][4];
const int u = 1, d = 2, c = 3; int main(){
LL n;
while(~scanf("%lld", &n)){
memset(dp,0,sizeof(dp));
for(int i = 1; i <= n; i++) {
scanf("%lld%lld%lld", &subway[i].U, &subway[i].D, &subway[i].C);
} for(int i=1;i<=n;i++){
dp[i][u] = min(dp[i-1][d],dp[i-1][c])+subway[i].U;
dp[i][d] = min(dp[i-1][u],dp[i-1][c])+subway[i].D;
dp[i][c] = min(dp[i-1][u],dp[i-1][d])+subway[i].C;
} LL ans = min(dp[n][u],dp[n][d]);
ans = min(ans, dp[n][c]);
printf("%lld\n", ans);
}
return 0;
}


对比两个for循环,我们发现其实两个for循环的每一步其实是对应的,也就是说,每个subway[i]只在第i次循环中被调用,所以我们可以考虑将两个循环合并成一个。

for(int i = 1; i <= n; i++) {
scanf("%lld%lld%lld", &subway[i].U, &subway[i].D, &subway[i].C); dp[i][u] = min(dp[i-1][d],dp[i-1][c])+subway[i].U;
dp[i][d] = min(dp[i-1][u],dp[i-1][c])+subway[i].D;
dp[i][c] = min(dp[i-1][u],dp[i-1][d])+subway[i].C;
}

合并循环之后发现,每组subway[i].Usubway[i].D, subway[i].C都是只在该次循环用到,以后不会再用了;而且由于是合在一个循环里,我们没有必要把它存起来以在第二个循环重新调用。也就是说,在这种情况下subway这个数组和Subway结构体的定义完全是多余的,我们完全可以直接删掉,改成用U、D、C三个变量当缓存就行了。

合循环、去数组,这样子我们就将时间复杂度和空间复杂度都降低了一半左右。

时间复杂度为 \(O(n)\)

空间复杂度为 \(O(n)\) , 具体大约是 \(4n\)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std; const int MAXN = 200005;
LL dp[MAXN][4];
const int u = 1, d = 2, c = 3; int main(){
LL n;
while(~scanf("%lld", &n)){
LL U,D,C;
memset(dp,0,sizeof(dp));
for(int i = 1; i <= n; i++) {
scanf("%lld%lld%lld", &U, &D, &C); dp[i][u] = min(dp[i-1][d] , dp[i-1][c]) + U;
dp[i][d] = min(dp[i-1][u] , dp[i-1][c]) + D;
dp[i][c] = min(dp[i-1][u] , dp[i-1][d]) + C;
}
LL ans = min(dp[n][u],dp[n][d]);
ans = min(ans, dp[n][c]);
printf("%lld\n", ans);
}
return 0;
}


那么,还能不能再优化呢?当然可以!

看上面的代码,会发现dp[i]只与dp[i-1]有关,是Markov链,无后效性,dp[i-2]及以前的都无用了,那么我们可以考虑用滚动数组来改进程序。

简单来说,滚动数组就是让数组滚动起来,每次都使用固定的几个存储空间,来达到压缩,节省存储空间的作用。

可以看到,用滚动数组改进之后的程序在空间上不再受n的限制,无论n多大都能处理,有效防止MLE。

时间复杂度为 \(O(n)\) (时间复杂度是改不动的,虽然可以用计组的知识继续优化,但是没多大效果)

空间复杂度为 \(O(1)\)

(另外const int u = 1, d = 2, c = 3;能帮助你在编写程序的过程中更容易地理清思路)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std; LL dp[2][3];
const int u = 0, d = 1, c = 2; int main(){
int n;
while(~scanf("%d", &n)){
dp[0][u] = dp[0][d] = dp[0][c] = 0;
LL U,D,C;
for(int i = 1; i <= n; i++){
scanf("%lld%lld%lld", &U, &D, &C); dp[1][u] = min(dp[0][d], dp[0][c]) + U;
dp[1][d] = min(dp[0][u], dp[0][c]) + D;
dp[1][c] = min(dp[0][u], dp[0][d]) + C; dp[0][u] = dp[1][u];
dp[0][d] = dp[1][d];
dp[0][c] = dp[1][c];
}
LL ans = min(dp[0][u], dp[0][d]);
ans = min(ans, dp[0][c]);
printf("%lld\n", ans);
}
return 0;
}

至此,主要的优化工作就结束了。

最后,如果不是老手的话直接想到最后一个版本还是有些难度的,所以一开始不妨先想个naïve(暴力)点的版本再逐模块地优化。

buaacoding_2018算法期末上机G题.地铁建设题解的更多相关文章

  1. 2016级算法期末上机-G.中等·Bamboo's Fight with DDLs II

    中等·Bamboo's Fight with DDLs II 分析 一句话:给定字符串,求最长回文子序列长度,动态规划LCS思想的进阶应用 具体思路如下: 对于任意字符串,如果头尾字符相同,那么字符串 ...

  2. 2016级算法期末上机-H.难题·AlvinZH's Fight with DDLs III

    1119 AlvinZH's Fight with DDLs III 思路 难题,最小点覆盖. 分析题意,某一个任务,既可以在笔记本A的 \(a\) 模式下完成,也可以在笔记本B的 \(b\) 模式下 ...

  3. 2016级算法期末上机-D.简单·AlvinZH's Fight with DDLs I

    1117 AlvinZH's Fight with DDLs I 思路 简单题,动态规划. 本题与期末练习赛B题很相似,而且更为简单些.简化问题:在数字序列上取数,不能取相邻的数. DP数组定义,dp ...

  4. 2016级算法期末上机-B.简单·ModricWang's Fight with DDLs I

    1124 ModricWang's Fight with DDLs I 思路 这道题本质上就是一个多项式求值,题目中的n需要手动算一下,单位复根可以根据复数的性质来求,即\(e^{i\pi}+1=0\ ...

  5. 北航2018级算法期末上机实录随笔1st

    简单记录下题目类型和做题情况,理性复习同时也希望提供一些参考 题目描述 共计八个题目,按照助教的划分,题目分类如下 一个签到(二分查找),两个板子(活动选择.KMP(洛谷kmp模板题)),一个板子变形 ...

  6. 2016级算法期末上机-F.中等·AlvinZH's Fight with DDLs II

    1118 AlvinZH's Fight with DDLs II 思路 中等题,贪心. 理解题意,每次攻击中,可以使某个敌人生命值-1,自己生命值减去∑存活敌人总攻击力. 贪心思想,血量少攻击高的要 ...

  7. 集训第四周(高效算法设计)G题 (贪心)

    G - 贪心 Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Desc ...

  8. 2016级算法期末上机-I.难题·ModricWang's Fight with DDLs III

    1126 ModricWang's Fight with DDLs III 思路 由于题目中已经说明了时间经过了正无穷,因此初始位置是不重要的,并且每条边.每个点的地位是均等的.因此到达每个点的概率就 ...

  9. 2016级算法期末上机-E.中等·ModricWang's Fight with DDLs II

    1125 ModricWang's Fight with DDLs II 思路 圆内被划分部分数的计算方式如下: 圆内部的每一个交点都使得总份数增加了一:除此之外,每一根直线段最后抵达圆周时,总份数也 ...

随机推荐

  1. Django ORM存储datetime 时间误差8小时问题

    今天使用django ORM 将获取到的时间入库,并未出现问题,但是后来发现时间晚了8小时,经查询Django官方文档发现获取本地时间和UTC时间有差别. 首先科普下:UTC是协调世界时 UTC相当于 ...

  2. 从手机中提取boot.img

    测试环境:nexus 5,android 6.0 怕出问题可以先用TWRP备份 adb shell su cd /dev/block/platform/msm_sdcc./by-name ls -l ...

  3. log4cplus在VS项目中的使用

    log4cplus是C++编写的开源的日志系统,宣称具有线程安全.灵活.以及多粒度控制的特点,通过将日志划分优先级使其可以面向程序调试.运行.测试.和维护等全生命周期.你可以选择将日志输出到屏幕.文件 ...

  4. 发布到FaceBook试玩广告,FaceBook要求要一个Html文件

    Facebook 试玩广告具体要求: 试玩广告参数是创建试玩广告素材时要满足的要求. 试玩素材应为 HTML5 格式. 试玩广告素材不应使用 mraid.js 格式. 包含所有素材的试玩广告的单个 H ...

  5. mybatis增强

    MyBatis SQL参数传递(掌握) SQL映射器Mapper接口(掌握)Myb atis批量操作(理解掌握) (多对一)关联映射(掌握) (一对多,多对多)集合映射 MyBatis原理回顾(Obj ...

  6. 纯JS.CSS编写的可拖拽并左右分栏的插件(复制代码就能用)

    <!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equ ...

  7. python爬虫中scrapy框架是否安装成功及简单创建

    判断框架是否安装成功,在新建的爬虫文件夹下打开盘符中框输入cmd,在命令中输入scrapy,若显示如下图所示,则说明成功安装爬虫框架: 查看当前版本:在刚刚打开的命令框内输入scrapy versio ...

  8. 搭建apache本地服务器·Win

    1.下载apache地址:https://www.apachelounge.com/download/ 注意:下载压缩包如下 httpd-2.4.37-win64-VC15.zip 其中根据自己电脑的 ...

  9. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'multipartResolver': Failed to introspect bean class [org.springframework.web.multipart.commons.CommonsMultipartR

    在用spring mvc 做文件上传的时候出现了这个问题(能看到这篇文章就说明你已经有了那两个包了) 错误:org.springframework.beans.factory.BeanCreation ...

  10. PAT (Basic Level) Practice (中文)1023 组个最小数

    1023 组个最小数 给定数字 0-9 各若干个.你可以以任意顺序排列这些数字,但必须全部使用.目标是使得最后得到的数尽可能小(注意 0 不能做首位).例如:给定两个 0,两个 1,三个 5,一个 8 ...