当前位置: 首页 > news >正文

题解:P14556 [ROI 2013 Day2] 星际航程

这道题是一道有一定思维难度的 DP 题目。

首先讲 \(\Theta(n^2)\) 做法:预处理每一个行星最远能够直接到达的那个行星,记作 \(nxt_i\)。接着上转移方程。设 \(f_i\) 为到达第 \(i\) 个行星需要执行的最小加注次数。初始 \(f_1=0,f_2=f_3=\cdots=f_n=\infty\) 容易列出转移方程:

\(f_j=\min(f_j,f_i+1)\quad({i+1\leq j \leq nxt_i})\)

根据题意,第一问的答案为 \(ans=f_n\)。但如果 \(f_n=inf\),则说明无解,要输出数字 \(0\)

马上写出代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 5;
const int inf = 1e9 + 7;
int n;
int a[N];
int pre[N], nxt[N];
int f[N];
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];}for (int i = 1; i <= n; i++) {nxt[pre[a[i]]] = i;pre[a[i]] = i;}f[0] = 0;for (int i = 2; i <= n; i++) {f[i] = inf;}int last = 0;for (int i = 1; i <= n; i++) {for (int j = i + 1; j <= nxt[i]; j++) {if (f[i] + 1 < f[j]) {f[j] = f[i] + 1;}}}if (f[n] == inf) {cout << 0 << endl;return 0;}cout << f[n] << endl;return 0;
}

但这里第二问怎么办呢?我们在 DP 的时候记录 \(frm_i\),表示可以到达第 \(i\) 个行星的距离最近那个行星。然后记录一个变量 \(now=n\) 和一个栈 \(ans\)。让 \(now=frm[now]\),随后将 \(now\) 压入 \(ans\),一直进行这个操作直到 \(frm[now]=0\)。最后只需要输出 \(ans\) 里的值就可以了。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 5;
const int inf = 1e9 + 7;
int n;
int a[N];
int pre[N], nxt[N];
int frm[N];
int f[N];
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];}for (int i = 1; i <= n; i++) {nxt[pre[a[i]]] = i;pre[a[i]] = i;}f[0] = 0;for (int i = 2; i <= n; i++) {f[i] = inf;}int last = 0;for (int i = 1; i <= n; i++) {for (int j = max(last, i + 1); j <= nxt[i]; j++) {if (f[i] + 1 < f[j]) {f[j] = f[i] + 1;frm[j] = i;last = j;}}}if (f[n] == inf) {cout << 0 << endl;return 0;}cout << f[n] << endl;stack<int> ans;int now = n;while (frm[now]) {now = frm[now];ans.push(now);}while (!ans.empty()) {cout << ans.top() << " ";ans.pop();}cout << endl;return 0;
}

且慢!这么做只能获得 50pts。为什么?因为 \(n\leq30000\),而我们的时间复杂度是 \(\Theta(n^2)\)

考虑优化:我们容易发现 \(f\) 单调不降(应该是可以使用斜率优化的,但比较慢),于是记录一个 \(last\) 为当前已经计算过的行星的编号最大值。这样我们每次遍历 \(j\) 的时候起点变成了 \(\max(i+1,last)\)(保持正确性的原因是如果前面计算过它,后面再次计算一定是不优的)。

代码如下:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5 + 5;
const int inf = 1e9 + 7;
int n;
int a[N];
int pre[N], nxt[N];
int frm[N];
int f[N];
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];}for (int i = 1; i <= n; i++) {nxt[pre[a[i]]] = i;pre[a[i]] = i;}f[0] = 0;for (int i = 2; i <= n; i++) {f[i] = inf;}int last = 0;for (int i = 1; i <= n; i++) {for (int j = max(last, i + 1); j <= nxt[i]; j++) {if (f[i] + 1 < f[j]) {f[j] = f[i] + 1;frm[j] = i;last = j;}}}if (f[n] == inf) {cout << 0 << endl;return 0;}cout << f[n] << endl;stack<int> ans;int now = n;while (frm[now]) {now = frm[now];ans.push(now);}while (!ans.empty()) {cout << ans.top() << " ";ans.pop();}cout << endl;return 0;
}

结果你发现:这个代码时间复杂度是 \(\Theta(n)\) 的,AC 了。

http://www.jsqmd.com/news/433026/

相关文章:

  • 题解:UVA11350 Stern-Brocot Tree
  • 数字孪生架构设计及系统开发难点有哪些?
  • ansible常见的模块
  • java学习笔记1.16
  • VBA 64位API声明语句第018讲
  • Lotus扩散模型深度估计精研
  • Mask2Former实例分割实战:Swin大模型解析[特殊字符]
  • 【电力系统】MARS模型参考自适应、SMO滑模观测器永磁同步电机对比仿真模型
  • 保险公司做养老有什么优势?从大家保险“城心2.0”看服务体系构建
  • 大数据领域分布式计算的技术峰会亮点
  • INI 文件超详细入门到实战教程
  • MGM-Omni-TTS语音模型入门指南 [特殊字符]
  • C# .NET 周刊|2026年1月4期
  • 基于MPC模型预测改进PMSM-MRAS模型参考自适应无感观测仿真
  • MioCodec音频编解码器:高效语音处理新方案
  • 交期慢?质量参差?成本高?一文讲清供应商全生命周期管理!
  • BPE分词器实现
  • 新鲜出炉!2026徐汇专家推荐服务优的宠物医院排行,狗狗耳道内窥镜检查/宠物绝育/狗狗隐睾绝育,宠物医院专家找哪个 - 品牌推荐师
  • 主机清单和ad-hoc
  • 2026年3月光纤激光切管机厂家推荐,资质案例售后机构深度解读 - 品牌鉴赏师
  • 折扣影票api接口对接的详细操作指南
  • Mask2Former-Swin城市景观数据集图像分割模型[特殊字符]
  • 11个免费开源后台管理系统模板
  • Mask2Former图像分割全攻略:从Swin架构到COCO实战应用 [特殊字符]
  • 刷榜冠军秒变“删库侠“?揭秘AI基座模型失控的惨烈真相!
  • Docker Desktop(详细使用流程)
  • 游戏人物移动效果对应实际刷新率对比与Client-side Prediction Interpolation调整优化
  • DeepSeek V4,下周正式登场!
  • Mask2Former图像分割技术解析[特殊字符]
  • 2026年3月手持激光焊机厂家推荐,产能专利环保三维数据全面透视 - 品牌鉴赏师