CF201C Fragile Bridges TJ
本题解依旧发布于洛谷,如果您能点个赞的话……(逃
前言
正解:动态规划
思路不是很好想,想出来了应该就没有多大问题了,但是需要处理的细节较多,再加上水水的样例,难度应该是偏难的。个人感觉应该是绿到蓝的亚子。
先说说思路的来源(错误解法,不想看请跳过)
中午打开题目,第一反应:这不是一个暴搜部分分到手吗?
打了个无脑暴搜,深搜+回溯,分别向往左走和往右走的方向进行搜索,只是需要注意变量初始化和判断是否可以走。
时间复杂度 \(Θ(2^{n})\),校内 OJ \(15pts\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,ans,a[100005];
int dfs(int i,int sum){
int left=sum,right=sum;
if(!a[i-1]&&!a[i+1]) return sum;
if(a[i-1]){ //可以往左走
a[i-1]--; //要耗费一次桥的耐久度
left=dfs(i-1,sum+1);
a[i-1]++; //回溯
}
if(a[i+1]){ //可以往右走
a[i+1]--; //要耗费一次桥的耐久度
right=dfs(i+1,sum+1);
a[i+1]++; //回溯
}
return max(left,right); //返回最大值
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n-1;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n-1;i++){
ans=max(ans,dfs(i,0));
}
printf("%lld",ans);
return 0;
}
可是 CF 的题都是绑在一个点上的啊,于是还没把正解想出来的我开始想怎么优化。
我先想到了一个假优化:使用一个前缀和 \(pre\) 数组统计一下前面最多可能得多少分,如果现在得的分 + 往左/右走能拿到的最多的分都不可以更新答案的话就不用搜这一边了。
程序不做展示,但是我发现样例竟然没过……
淦,那到底是哪里出问题了呢?
经过短暂的思考我们可以发现,完全有这么一种可能:
假设你现在从第 \(i\) 个平台往左走,在左边尽量赚到最多的分数之后回到第 \(i\) 个平台,然后再往右边走,再在右边的平台结束游戏。
此时的得分完全是有可能高于 \(pre_{i-1}+sum\) 或 \(ans\) 的。
正解
上面的枝我们减错了,但是事情却有了一点眉目:
这道题好像每次的状态转移都与他离开之后回不回来有关系。
为了方便,我们将第 \(i\) 与 \(i+1\) 个平台中的桥定义为 \(i\) 号桥,这也与输入的数组下标对应。
所以可以定义 \(dp\) 数组意义如下:
\(dp_{i,0}\) 代表往左边走且不回来。
\(dp_{i,1}\) 代表往左边走且必须回来。
\(dp_{i,2}\) 代表往右边走且不回来。
\(dp_{i,3}\) 代表往右边走且必须回来。
PS:关于必须回来这一个点,现有的题解说的是不保证回来,那么就有两种情况,较难判断。所以设置绝对一点较好思考。
首先思考 \(dp_{i,0}\) 的情况。如果我们从第 \(i\) 个平台往左边走(而且可以走),就一定会走到第 \(i-1\) 个桥上。
而因为不回第 \(i\) 个平台,所以我们无需考虑到了第 \(i-1\) 个平台后再往左走(不考虑直接返回),还能不能回到第 \(i-1\) 的平台。因此,对于走到第 \(i-1\) 个平台能获得的最大分数,就为 \(dp_{i-1,0}\) 和 \(dp_{i-1,1}\) 的最大值。
然后再求从第 \(i\) 个平台到第 \(i-1\) 个平台能获得的最大分数。
首先要明确,我们是不回来的,所以最优的方式就是把这座桥尽量“榨干”,即能走断就走断,在两个平台之间反复横跳。但是若是要走到对面,需要走桥的次数必须是奇数。所以,如果桥当前还能走的最大次数是偶数,那就必须留一次走的机会(不然就走回去走不过来了)。
所以可以得出,往返这两个平台间能获得的最大分数(暂时叫 \(score\))为:
\begin{cases}
a_{i-1},2\not|\;a_{i-1}\\
a_{i-1}-1,2\;|\;a_{i-1}
\end{cases}
\]
在 c++ 中,我们可以用更加简洁的三目运算符表示。
dp[i][0]=max(dp[i-1][0],dp[i-1][1])+((a[i-1]&1)?a[i-1]:(a[i-1]-1));
接着看必须回来的情况。如果要保证回到第 \(i\) 个平台,那么必须保证要先回到第 \(i-1\) 个平台,然后还要能从中间那座桥走过去。所以前面加上的是 \(dp_{i-1,1}\),没有别的情况。
像之前那样考虑在中间走能拿到的最大分数。因为要回去,我们需要经过偶数次桥。如果当前桥能走的次数是奇数,那就只能浪费一次机会达到能回来的目的。
\begin{cases}
a_{i-1}-1,2\not|\;a_{i-1}\\
a_{i-1},2\;|\;a_{i-1}
\end{cases}
\]
同样可以用三目运算符解决。
dp[i][1]=dp[i-1][1]+((a[i-1]&1)?(a[i-1]-1):a[i-1]);
不过这样还是不对,大家是否注意到这个程序没有关于无法走动的判断?现在这个判断来了!
如果这座桥能走的次数为 \(1\),那我走过去这座桥就断了,无法回来。为此,我只能放弃前面的所有分数。
所以上述程序只有在 \(a_i>1\) 时才能执行,当 \(a_i=1\) 时,\(dp_{i,1}\) 的值为 \(0\)(不用赋值)。
右边同理。只是循环要倒着枚举而已。
最后我们需要知道答案时什么。因为每一个平台都有可能作为出发点,所以需要枚举求解最优解。
那么,对于每一个点,我们可以先往左边走回来再往右边不回来,也可以先往右边回来再往左边不回来。所以答案就为:
\]
提示:打代码的时候一定要注意你的这个下标到底代表的是平台还是桥! 在这道题中,\(dp\) 数组代表的是平台,\(a\) 数组代表的是桥。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
long long ans,n,a[100005],dp[100005][4];
int main(){
scanf("%lld",&n);
for(int i=1;i<=n-1;i++) scanf("%lld",&a[i]);
for(int i=2;i<=n-1;i++){
dp[i][0]=max(dp[i-1][0],dp[i-1][1])+((a[i-1]&1)?a[i-1]:(a[i-1]-1));
if(a[i-1]>1) dp[i][1]=dp[i-1][1]+((a[i-1]&1)?(a[i-1]-1):a[i-1]); //注意加特判
}
for(int i=n;i;i--){
dp[i][2]=max(dp[i+1][2],dp[i+1][3])+((a[i]&1)?a[i]:(a[i]-1));
if(a[i]>1) dp[i][3]=dp[i+1][3]+((a[i]&1)?(a[i]-1):a[i]);
}
for(int i=1;i<=n;i++) ans=max(ans,max(dp[i][0]+dp[i][3],dp[i][1]+dp[i][2]));
printf("%lld",ans);
return 0;
}
比暴力还短吼
提交记录作证 qwq:link
CF201C Fragile Bridges TJ的更多相关文章
- Codeforces Round #127 (Div. 1) C. Fragile Bridges dp
C. Fragile Bridges 题目连接: http://codeforces.com/contest/201/problem/C Description You are playing a v ...
- Codeforces Round #127 (Div. 2)
A. LLPS 长度最大10,暴力枚举即可. B. Brand New Easy Problem 枚举\(n\)的全排列,按题意求最小的\(x\),即逆序对个数. C. Clear Symmetry ...
- TJ/T808 终端通讯协议设计与实现(码农本色)
由于公司项目涉及到相关技术,对于平常写WEB的技术人员来说对这人来说比较默生:为了让下面的技术人员更好地对这个协议的实施,所以单独针对这个协议进行了分析和设计,以更于后期更好指导相关开发工作.由于自己 ...
- hdu 4738 Caocao's Bridges 图--桥的判断模板
Caocao's Bridges Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- POJ2288 Islands and Bridges
Description Given a map of islands and bridges that connect these islands, a Hamilton path, as we al ...
- HDU 4738 Caocao's Bridges(Tarjan求桥+重边判断)
Caocao's Bridges Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- HDU 4738 Caocao's Bridges
Caocao's Bridges Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- ZOJ 2588 Burning Bridges(求含重边的无向连通图的割边) - from lanshui_Yang
Burning Bridges Time Limit: 5 Seconds Memory Limit: 32768 KB Ferry Kingdom is a nice little country ...
- zoj 2588 Burning Bridges【双连通分量求桥输出桥的编号】
Burning Bridges Time Limit: 5 Seconds Memory Limit: 32768 KB Ferry Kingdom is a nice little cou ...
随机推荐
- 注解式项目开发!详细解析Java中各个注解的作用和使用方式
@Target 作用: 指明了修饰的这个注解的使用范围, 即被描述的注解可以用在哪里 @Target(ElementType.Type) ElementType取值的类型: TYPE: 类,接口或者枚 ...
- VRRP协议与原理
VRRP协议与原理 目录: 一.VRRP协议概述 1.1.VRRP协议 1.2.单网关和多网关的缺陷 1.3.VRRP基本概述 二.VRRP工作原理 2.1.VRRP主备份备份工作工程 2.2.VRR ...
- 上手 Raspberry Pi Pico
什么是 PICO Raspberry Pi Pico 是树莓派推出的一块基于 Arm Cortex-M0+ 内核的 MCU 的开发板,使用的 MCU 是树莓派自己研发的 Arm Cortex-M0+ ...
- 『心善渊』Selenium3.0基础 — 13、Selenium操作下拉菜单
目录 1.使用Selenium中的Select类来处理下拉菜单(推荐) 2.下拉菜单对象的其他操作(了解) 3.通过元素二次定位方式操作下拉菜单(重点) (1)了解元素二次定位 (2)示例: 页面中的 ...
- 痞子衡嵌入式:以i.MXRT1xxx的GPIO模块为例谈谈中断处理函数(IRQHandler)的标准流程
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是以i.MXRT的GPIO模块为例谈谈中断处理函数(IRQHandler)的标准流程. 在痞子衡旧文 <串口(UART)自动波特率识 ...
- 即时通信之 SignalR
即时通信在日常的web开发场景中经常使用,本篇主要回顾一下SignalR的实现原理和通过例子说明如何在.NET Core 中使用. SingnalR 应用 需要从服务器进行高频更新的应用程序.例如游戏 ...
- oracle :如何测试数据库安装是否成功
要测试数据安装是否成功,可按顺序执行以下两个步骤: 测试步骤 1: 请执行操作系统级的命令: tnsping orcl (如果出现[TNS-03505:无法解析名称]的提示错误: 那就改为tnspi ...
- layui laydate 设置日期格式 最大值等
laydate.render({ elem: "#jhsj", format: 'yyyy-MM', type: 'month', //显示月份 year 显示到年 max : & ...
- SpringBoot集成logback后访问日志端点
问题描述 使用SpringBootAdmin(sba)监控Springboot服务时,配置了logback日志框架,按天滚动生成日志,此时在sba的日志监控页面出现404,如下图所示: 解决方案 查看 ...
- 6-x1 read命令:从键盘读取数据
1.read的用法 read从 STDIN 读取一行数据并将其赋给一个变量,如果没有进行重定向,默认就是从键盘读取用户输入的数据:如果进行了重定向,那么可以从文件中读取数据. read 命令的用法为: ...