news 2026/10/8 12:06:46

poj 1613 Cave Raider 用 SPFA 求最短路:TaoToken 统一 Key 跑通样例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
poj 1613 Cave Raider 用 SPFA 求最短路:TaoToken 统一 Key 跑通样例

1. POJ 1613 Cave Raider 到底在考什么:带时间窗的最短路建模

POJ 1613 Cave Raider 这道题,第一次读题的人十有八九会被那一长串「关闭时间、打开时间」绕晕。它本质上是一道最短路题,但和普通 Dijkstra 模板题不一样的地方在于:边的可用性随时间变化。你可以把它理解成一条隧道像地铁闸机,某些时间段闸机关闭,你正好走到一半就会被夹住,所以出发前必须算清楚「现在进去,能不能在闸机关闭前走出来」。

题目给的信息是:n 个洞穴(n ≤ 50),m 条隧道(m ≤ 500),起点 s,终点 t,起始时间为 0。每条隧道有三个基础属性:两个端点、通过耗时 w,后面跟着一串递增的正整数,交替表示「关闭时刻」和「打开时刻」。比如10 14 5 6 7 8 9,意思是隧道连接 10 和 14,走完要 5 个单位时间,6 时刻关闭、7 时刻打开、8 时刻又关闭、9 时刻又打开,9 之后永远打开。

这里有个关键细节:关闭到打开这段时间隧道在「清理」,人在里面会死。所以你不能在关闭时刻正好卡在隧道里。换句话说,如果你在时刻costu到达隧道入口,想通过这条耗时 w 的隧道,必须满足「进入后到走完的整个区间 [costu, costu+w] 都落在某个开放区间内」。

我试过直接套普通 SPFA,结果样例第一个就 WA,原因就是没处理这个时间窗。正确的松弛条件是:dist[v] > dist[u] + edge(u,v),但这个edge(u,v)不是固定值,而是「在 dist[u] 时刻从 u 出发,最早能到达 v 的时刻」。这个值需要针对每条边的开放区间单独算。

适合谁看:正在刷 POJ / 准备算法竞赛、已经会写 SPFA 模板但卡在这道题时间窗处理上的同学。核心检索词就是「POJ 1613 SPFA 最短路 时间窗」,下面我会把建图、时间窗计算、SPFA 队列模板、样例验证一步步拆开,最后用 TaoToken 统一 Key 调模型帮你核对复杂度和定位 WA/TLE。

先说清楚为什么用 SPFA 而不是 Dijkstra:因为这里的「边权」依赖于到达时间,不是静态的,Dijkstra 的贪心前提被破坏,而 SPFA 的队列松弛天然适合这种「到达时间变了就要重新松弛邻居」的场景。n 只有 50,m 最多 500,SPFA 完全够用,不用担心被卡。

2. TaoToken 统一 Key 前置:把模型调试通道接进来

写这道题的时候,最容易卡住的不是 SPFA 本身,而是两个地方:一是时间窗的边界判断(<=还是<),二是样例输出对不上时不知道错在哪。这时候用 TaoToken 统一 Key 调模型帮你逐行核对逻辑,比自己在纸上推快得多。

TaoToken 是一个统一的大模型 API 接入通道,你拿一个 Key 就能调用多种模型,用来做代码审查、复杂度核对、报错解释都很顺手。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api (这个不加 UTM)。

前置准备分三步:

第一步,注册后在控制台创建 API Key。控制台地址 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,进去后找到 API Keys 页面,新建一个 Key,复制保存。注意 Key 只在创建时完整显示一次,丢了就得重建。

第二步,确认你要用的模型 ID。TaoToken 支持对话模型和编码模型,做算法调试我一般用编码能力强的模型。模型列表可以在文档里查:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。

第三步,把 Base URL、Key、Model ID 三件套配好。如果你用 Cline、CC Switch 这类工具,配置项就是这三个。下面给一个通用的 JSON 配置片段,路径按你实际工具放:

{ "provider": "taotoken", "baseUrl": "https://taotoken.net/api", "apiKey": "sk-你的TaoToken密钥", "model": "你的模型ID", "temperature": 0.2 }

如果你用的是 Claude Code 这类命令行工具,配置思路一样,把 Base URL 指向https://taotoken.net/api,Key 填进去,Model ID 选编码模型即可。想直接在线对话验证模型是否通,可以打开模型对话页:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 。

这里提醒一句:TaoToken 是统一 API 通道,不是让你绕过什么限制,它只是把多个模型的调用收敛到一个 Key 上,方便你在刷题时随时切模型做代码审查。配好之后,你就可以把 POJ 1613 的代码贴进去,让模型帮你检查时间窗边界。

3. 可复制配置:建图结构 + 时间窗函数 + SPFA 模板

这一节是全文核心,我把 POJ 1613 的完整可复制实现拆成三块:建图、时间窗计算、SPFA。你直接抄进 C++ 就能跑。

先说建图。因为两个洞穴之间可能有多条隧道,而且同一条隧道两个方向都能走,所以用vector<Node> v[maxn][maxn]存,v[u][v]表示从 u 到 v 的所有隧道。每个 Node 存通过耗时 w、开放区间数组 a、区间数量 sz。

#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <vector> #define maxn 55 using namespace std; const int INF = 0x3f3f3f3f; int n, m, sx, ex; bool vis[maxn]; int dist[maxn]; char s[1000005]; struct Node { int w, sz; int a[40]; // a[0]=0, 之后成对存 关闭/打开 时刻 } cur; vector<Node> v[maxn][maxn]; queue<int> q;

读入部分要特别小心,因为每行隧道信息的整数个数不固定(最多 35 个),所以用gets读整行再手动解析。解析时把数字依次取出:前三个是 u、v、w,后面全是时间点。这里有个坑:时间点个数可能是奇数,表示最后一个关闭时刻之后永远关闭;也可能是偶数,表示最后一个打开时刻之后永远打开。代码里用(k-3)%2判断,偶数就补一个 INF 表示「之后一直开着」,奇数就保持原样表示「之后一直关着」。

void read() { int i, k = 0, t = 0, uu, vv, w, flag = 0, len; len = strlen(s); s[len] = 'x'; s[len+1] = '\0'; for (i = 0; s[i] != '\0'; i++) { if (s[i] >= '0' && s[i] <= '9') { flag = 1; t = t * 10 + s[i] - '0'; } else { if (flag) { k++; flag = 0; if (k == 1) uu = t; else if (k == 2) vv = t; else if (k == 3) { w = t; cur.w = w; } else cur.a[k-3] = t; t = 0; } } } cur.a[0] = 0; if ((k - 3) % 2 == 0) { cur.a[k-2] = INF; cur.sz = k - 2; } else cur.sz = k - 3; v[uu][vv].push_back(cur); v[vv][uu].push_back(cur); }

时间窗计算函数getcost是整道题最容易写错的地方。它的输入是:从 uu 到 vv 的第 k 条隧道、当前到达 uu 的时刻 costu。输出是「最早能到达 vv 的时刻」,如果这条隧道在当前时刻无法通过,返回 -1。

逻辑是遍历所有开放区间[a[i], a[i+1]](i 从 0 开始每次加 2):

  • 如果costu > a[i+1],说明这个区间已经过去了,continue;
  • 如果costu落在[a[i], a[i+1]]内,且costu + w <= a[i+1],说明现在进去能在关闭前出来,直接返回costu + w;
  • 否则(costu 在区间之前,或者当前区间放不下),检查这个开放区间的长度a[i+1] - a[i]是否 >= w,如果够长,就等到a[i]再进,返回a[i] + w。
int getcost(int uu, int vv, int k, int costu) { int i, sz = v[uu][vv][k].sz; for (i = 0; i <= sz; i += 2) { if (costu > v[uu][vv][k].a[i+1]) continue; else if (costu <= v[uu][vv][k].a[i+1] && costu >= v[uu][vv][k].a[i]) { if (costu + v[uu][vv][k].w <= v[uu][vv][k].a[i+1]) return costu + v[uu][vv][k].w; } else { if ((v[uu][vv][k].a[i+1] - v[uu][vv][k].a[i]) >= v[uu][vv][k].w) return v[uu][vv][k].a[i] + v[uu][vv][k].w; } } return -1; }

SPFA 主体:从起点入队,每次取出 nx,遍历所有可能的邻居 i,对v[nx][i]里的每条隧道算 getcost,取最小值 mi,如果dist[i] > mi就松弛并入队。

void SPFA() { int i, j, nx, sz, mi, cost; memset(vis, 0, sizeof(vis)); while (!q.empty()) q.pop(); dist[sx] = 0; vis[sx] = 1; q.push(sx); while (!q.empty()) { nx = q.front(); q.pop(); vis[nx] = 0; for (i = 1; i <= n; i++) { sz = v[nx][i].size(); if (!sz) continue; mi = INF; for (j = 0; j < sz; j++) { cost = getcost(nx, i, j, dist[nx]); if (cost != -1 && mi > cost) mi = cost; } if (dist[i] > mi) { dist[i] = mi; if (!vis[i]) { vis[i] = 1; q.push(i); } } } } }

主函数注意:scanf("%d",&n)读到 0 结束;每读完第一行四个整数后要gets(s)吃掉换行,再循环 m 次gets(s)读隧道。输出时dist[ex] < INF打印数值,否则打印*。

int main() { int i; while (scanf("%d", &n), n) { scanf("%d%d%d", &m, &sx, &ex); // init: 清空 vis/dist/v memset(vis, 0, sizeof(vis)); memset(dist, 0x3f, sizeof(dist)); for (i = 1; i <= n; i++) for (int j = 1; j <= n; j++) v[i][j].clear(); gets(s); for (i = 1; i <= m; i++) { gets(s); read(); } SPFA(); if (dist[ex] < INF) printf("%d\n", dist[ex]); else printf("*\n"); } return 0; }

这套代码的关键点就三个:v[u][v]存多条边、getcost处理时间窗、SPFA 里对每条边取最小到达时间。把这三块拼起来,样例就能过。

4. 验证请求与成功结果:样例输入输出逐行核对

代码写完别急着提交,先用题目给的样例跑一遍。样例输入比较长,我把它整理成可复制的形式,你直接存成in.txt:

2 2 1 2 1 2 5 4 10 14 20 24 30 1 2 6 2 10 22 30 6 9 1 6 1 2 6 5 10 1 3 7 8 20 30 40 2 4 8 5 13 21 30 3 5 10 16 25 34 45 2 5 9 22 32 40 50 3 4 15 2 8 24 34 4 6 10 32 45 56 65 5 6 3 2 5 10 15 2 3 5 2 9 19 25 2 2 1 2 1 2 7 6 9 12 1 2 9 8 12 19 0

编译运行:

g++ -O2 -o cave cave.cpp ./cave < in.txt

期望输出:

16 55 *

逐行解释一下这三个结果,方便你确认自己代码逻辑对不对:

第一组2 2 1 2,两条隧道都连接 1 和 2。第一条耗时 5,开放区间是 [0,4]、[10,14]、[20,24]、[30,∞)。第二条耗时 6,开放区间是 [0,2]、[10,22]、[30,∞)。从 1 出发时刻 0,走第一条:0 进 5 出,但 4 就关了,不行;等的话第一条要等到 10 才能进,15 出。走第二条:0 进 6 出,但 2 就关了,不行。所以最优是等第一条到 10 进,15 出?但答案是 16。再算:第二条在 [10,22] 开放,10 进 16 出,16 ≤ 22,可行,所以 16。这就是为什么答案是 16 而不是 15——第一条在 10 时刻的开放区间是 [10,14],长度 4 < 5,放不下,得等到 20。所以 16 是对的。

第二组答案是 55,第三组2 2 1 2两条隧道开放区间都很短,凑不出可行路径,输出*。

如果你跑出来第一个是 15,说明getcost里没检查区间长度是否够 w;如果第二个对不上,多半是时间窗边界<=写成了<。这两个是最常见的 WA 点。

想快速验证模型能不能帮你核对,可以把这段代码和样例贴到模型对话页 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite ,让它逐行走一遍 getcost,通常几秒就能指出边界问题。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth

刷这道题时,报错分两类:一类是算法本身的 WA/TLE,一类是调 TaoToken 时的接入报错。分开说。

算法侧最常见的三个坑:

第一个是getcost返回 -1 时没跳过,导致mi被错误更新。注意代码里if (cost != -1 && mi > cost),这个 -1 判断不能少,否则不可达的边会被当成有效值。

第二个是时间窗边界。题目说「关闭时刻到打开时刻之间在清理」,所以进入时刻 costu 必须满足costu >= a[i]且costu + w <= a[i+1]。如果你写成costu + w < a[i+1],就会漏掉「正好在关闭瞬间出来」的合法情况,样例第一组会算成 15 或更大。

第三个是读入。gets读整行,但第一行四个整数后面还有换行,必须先用一个gets(s)吃掉,否则第一条隧道信息会被当成空行。另外每行整数个数不固定,手动解析时k的计数要准,cur.a[0]=0这个补丁不能忘。

接入侧报错对照:

401 Unauthorized:Key 错了或没带。检查请求头里Authorization: Bearer sk-xxx,Key 从控制台 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 重新复制一份。

local proxy failed:本地代理配置和 Base URL 冲突。把 Base URL 直接设成https://taotoken.net/api,不要额外挂本地转发。

reading choices类报错:通常是响应体解析失败,多半是 Model ID 填错或模型不支持当前请求格式。去文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 核对模型 ID。

OAuth相关报错:如果你用的是 Claude Code 这类工具,OAuth 流程和 API Key 是两套。用 TaoToken 统一 Key 时,走 API Key 模式,别混用 OAuth 登录态。

TLE 的话,n=50、m=500,SPFA 最坏情况也不会超,POJ 给 1000MS 足够。如果你 TLE,检查是不是在 SPFA 里对每条边重复做了太多字符串解析——解析应该在读入阶段完成,SPFA 里只做数值计算。

6. 语义一致 CTA:把统一 Key 用在长期刷题与调试上

这道题跑通之后,你会发现真正花时间的不是 SPFA 模板,而是时间窗这种「题目特有的建模细节」。这类细节靠人眼盯很容易漏,用模型做代码审查就省事很多。

如果你只是偶尔调一次模型核对代码,用模型对话页就够了:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 。把代码和样例贴进去,让它逐行解释 getcost 的边界。

如果你在长期刷题、准备竞赛,或者要写 Agent 自动跑测试用例,建议用 Coding Plan,一个 Key 覆盖多种编码模型,切换不用重新配:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。

接入文档和 API Key 管理分别在这里:文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,API Keys https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。把 Base URL 固定成https://taotoken.net/api,Key 和 Model ID 配好,下次遇到 POJ 1613 这种时间窗最短路,直接让模型帮你核对 getcost 的区间判断,比反复提交试错快得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 12:05:50

无人机航拍人员数据集6442张VOC+YOLO格式

无人机航拍人员数据集6442张VOCYOLO格式数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;6442 标注数量(xml文件个数)&#xff1a;6442 标注数…

作者头像 李华
网站建设 2026/10/8 12:04:45

嵌入式开发提示工程实战:从“写个 GPIO 驱动“到精准 AI 代码生成

嵌入式开发提示工程实战:从"写个 GPIO 驱动"到精准 AI 代码生成 文章目录 嵌入式开发提示工程实战:从"写个 GPIO 驱动"到精准 AI 代码生成 一、引言:提示词决定了 AI 输出的上限 二、提示工程两大基本原则 2.1 清晰明确:任务三要素 2.2 角色设定:给 A…

作者头像 李华
网站建设 2026/10/8 12:02:21

本地大模型应用—solon-ai与MCP:把MCP endpoint改到TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 12:01:26

AI写论文哪个软件最好?先看清2026年的“游戏规则”

毕夏AI官网&#xff1a;www.bixiaai.com 微信公众号搜一搜&#xff1a;毕夏AI官网 如果你还在用“能不能生成一篇完整论文”作为评判AI写作软件的标准&#xff0c;那你可能已经落后于这个时代了。 2026年5月&#xff0c;中国学位与研究生教育学会正式发布了《规范研究生学位…

作者头像 李华