P1796 汤姆斯的天堂梦

记录89

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int dp[110],last[110];//last存前一行数据
int main() {
	int n,k,a,cost;
	last[1]=0;//初始化,等级0星球1
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>k;
		for(int j=1;j<=k;j++){
      dp[j]=INT_MAX;//初始化表格更新成最大值,相当于全新表格,之前的数据存last
			while(cin>>a&&a!=0){
				cin>>cost;
				dp[j]=min(dp[j],last[a]+cost);
			}//当前这一行每个格子的最小情况
		}
		for(int j=1;j<=k;j++) last[j]=dp[j];//将最小情况更新到last数组存储
	}
	int ans=INT_MAX;
	for(int i=1;i<=k;i++) ans=min(ans,dp[i]);
	cout<<ans;
    return 0;
}

题目传送门https://www.luogu.com.cn/problem/P1796


突破口

汤姆斯生活在一个等级为 0 的星球上。那里的环境极其恶劣,每天 12 小时的工作和成堆的垃圾让人忍无可忍。他向往着等级为 N 的星球上天堂般的生活。

有一些航班将人从低等级的星球送上高一级的星球,有时需要向驾驶员支付一定金额的费用,有时却又可以得到一定的金钱。

汤姆斯预先知道了从 0 等级星球去 N 等级星球所有的航线和需要支付(或者可以得到)的金钱,他想寻找一条价格最低(甚至获得金钱最多)的航线。


思路

🎯 问题本质

  • N+1 层星球:等级 0(起点) → 等级 1 → ... → 等级 N(终点)
  • 每一层 i(1 ≤ i ≤ N)有 K_i 个星球
  • 只能从等级 i−1 的星球飞到等级 i 的星球
  • 每条航线有费用(正 = 花钱,负 = 赚钱)
  • 目标 :从等级 0 的唯一星球(编号 1)出发,到达任意一个等级 N 的星球 ,使得总费用最小(可为负)

✅ 这是一个分层图上的最短路径问题 ,非常适合用 动态规划(DP) 解决。


🧠 动态规划设计

状态定义
  • dp[i][j]:到达等级 i、编号 j 的星球 所需的最小总费用
    (但本题只关心当前层和上一层,可用滚动数组优化)
初始状态
  • 等级 0 只有一个星球(编号 1),费用为 0:
    • last[1] = 0
状态转移

对等级 i 的每个星球 j

  • 遍历所有从等级 i−1 的星球 a(i, j) 的航线
  • 更新:

dpj=min⁡(dpj, lasta+cost)dpj=min(dpj, lasta+cost)

滚动数组优化
  • last[] 存储上一层(i−1)的最小费用
  • dp[] 计算当前层(i)的最小费用
  • 每层计算完后,last = dp
最终答案
  • min(dp[1..K_N])(到达等级 N 任一星球的最小费用)

代码分析

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int dp[110], last[110]; // 滚动数组:dp=当前层,last=上一层
  • 数组大小 110(因 K_i ≤ 100)
cpp 复制代码
int main() {
    int n, k, a, cost;
    last[1] = 0; // 等级0只有星球1,费用为0
cpp 复制代码
    cin >> n; // 总共要飞 N 层(从0→1→...→N)

🔁 逐层处理(从等级 1 到 N)

cpp 复制代码
    for(int i = 1; i <= n; i++){
        cin >> k; // 当前等级 i 有 k 个星球
cpp 复制代码
        for(int j = 1; j <= k; j++){
            dp[j] = INT_MAX; // 初始化当前星球 j 的费用为无穷大
cpp 复制代码
            while(cin >> a && a != 0){
                cin >> cost;
                dp[j] = min(dp[j], last[a] + cost);
            }
  • 读入所有从上一层星球 a当前 (i,j) 的航线
  • a 是上一层(i−1)的星球编号
  • cost 是该航线费用
  • 更新 dp[j] 为所有可能路径中的最小值

💡 输入格式:每行以 0 结束,例如 1 -5 2 10 0 表示:

  • 从上一层星球 1 来,费用 -5
  • 从上一层星球 2 来,费用 10

🔁 更新滚动数组

cpp 复制代码
        for(int j = 1; j <= k; j++)
            last[j] = dp[j]; // 将当前层结果存入 last,供下一层使用
    }

🔚 输出答案

cpp 复制代码
    int ans = INT_MAX;
    for(int i = 1; i <= k; i++)
        ans = min(ans, dp[i]);
    cout << ans;
    return 0;
}
  • 在最后一层(等级 N)的所有星球中找最小费用

🧪 样例验证(输入 #1)

等级结构:

  • 等级 0:1 个星球(编号 1)
  • 等级 1:2 个星球
  • 等级 2:3 个星球
  • 等级 3:2 个星球

初始化:

  • last[1] = 0

等级 1:

  • 星球 1:来自 0-1,费用 15 → dp[1]=15
  • 星球 2:来自 0-1,费用 5 → dp[2]=5
  • 更新 last = [15, 5]

等级 2:

  • 星球 1:来自 1-1(15-5=10),1-2(5+10=15)→ min=10
  • 星球 2:来自 1-1(15+3=18)→ 18
  • 星球 3:来自 1-2(5+40=45)→ 45
  • last = [10, 18, 45]

等级 3:

  • 星球 1:
    • 2-1: 10+1=11
    • 2-2: 18+5=23
    • 2-3: 45-5=40 → min=11
  • 星球 2:
    • 2-2: 18-19=-1
    • 2-3: 45-20=25 → min=-1

→ 最终答案 = min(11, -1) = -1


总结

要点 说明
模型 分层图最短路(DAG)
状态 last[j] = 上一层星球 j 的最小费用
转移 对每个当前星球,遍历所有上游航线,取最小
滚动数组 节省空间,只需保存上一层
复杂度 O(总航线数) ≤ 100×100×100 = 1e6

这段代码高效利用滚动数组和分层 DP,解决了多层星球间的最小费用路径问题。

相关推荐
wuyk55532 分钟前
98.C语言易混难点:字符数组与字符串指针的底层差异
c语言·开发语言·c++·stm32·嵌入式硬件·算法
峥无1 小时前
从0到1手撕红黑树:封装实现 my_map 与 my_set(SGI-STL 源码级深度解析)
开发语言·c++·笔记·算法·stl
波特率1152001 小时前
C++新特性---属性说明符与标准属性
开发语言·c++
不会代码的小猴1 小时前
3. 控件学习1
开发语言·c++·笔记·qt·算法
疯狂打码的少年3 小时前
【数据结构】图的存储结构:邻接矩阵与邻接表
数据结构·笔记
203号居民3 小时前
LeetCode hot 100 —41. 缺失的第一个正数
数据结构·算法·leetcode
专注仿真5 小时前
问答大模型技术方案算法实现-熵权法融合算法 + 交叉编码器重排算法
人工智能·python·算法·语言模型·问答大模型关键算法·熵权融合算法·交叉编码器重排算法
烧酒同学6 小时前
【C++】记录size of std::vector的巧妙坑
开发语言·c++·图形渲染
地平线开发者6 小时前
量化为什么会有损失:从量化误差到精度下降
算法