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,解决了多层星球间的最小费用路径问题。

相关推荐
Jerry9 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry11 小时前
LeetCode 459. 重复的子字符串
算法
哥不想学算法13 小时前
【C++】字符串字面量拼接
开发语言·c++
海石14 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石14 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry15 小时前
LeetCode 151. 反转字符串中的单词
算法
gugucoding16 小时前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL17 小时前
我的数据结构3——链表(link list)
数据结构·链表
a11177618 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜18 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai