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) 的航线
  • 更新:

dp[j]=min⁡(dp[j], last[a]+cost)dp[j]=min(dp[j], last[a]+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,解决了多层星球间的最小费用路径问题。

相关推荐
凌波粒2 小时前
LeetCode--19.删除链表的倒数第 N 个结点(链表)
java·算法·leetcode·链表
Lisssaa2 小时前
打卡第二十六天
c++
Fcy6482 小时前
与红黑树有关算法题
算法
Mem0rin2 小时前
[Java/数据结构]顺序表之ArrayList
java·开发语言·数据结构
ShineWinsu2 小时前
Anaconda被误删后的抢救手册大纲
数据结构
爱搞虚幻的阿恺2 小时前
UE入门-如何使用结构体数组创建动态UI
算法
4ever.ov02 小时前
定时器/时间轮
开发语言·c++·c·muduo·llinux
重生之我是Java开发战士2 小时前
【广度优先搜索】多源BFS:矩阵,飞地的数量,地图中的最高点,地图分析
数据结构·算法·矩阵·广度优先
sali-tec3 小时前
C# 基于OpenCv的视觉工作流-章43-轮廓匹配
图像处理·人工智能·opencv·算法·计算机视觉