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