1 条题解
-
0
自动搬运
来自洛谷,原作者为

Diamiko
此人已退役,不用留言了,看不见搬运于
2025-08-24 22:18:58,当前版本为作者最后更新于2020-03-31 13:36:10,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题解 P6245 【The Climbing Wall S】
核心算法:最短路
1.建模
我先来把问题简述一下: 在一个平面内,每个点都有其横坐标与纵坐标。Bessie想要从一个纵坐标不超过1000的点,爬到一个纵坐标为题目给定的H的点上。她每一步可以爬到与当前点距离不超过1000的点。问:最少爬多少步?
我们把题目中每一个落脚点就看作图中的点,把可以直接到达的两个点间建一条权值为1的边,代表走了一步。
那么跑一遍最短路,到终点的距离即为题目的答案。
2.细节
①建图
可能有些朋友已经注意到了,题目的起点和终点不唯一。
难道我们要跑多源最短路?那题目的数据范围显然是不允许的。
我们就要引入一种思想——虚拟点!
我自己乱起的名字Bessie 的起点可以在任一高度不超过的落脚点上。
也就是说,所有纵坐标小于等于1000的点都可以作为起点。
那么我们可以建立一个虚拟起点,它到所有可以作为起点的点之间都建立一条边,权值为0(代表不增加次数)。这样,我们就可以以这个虚拟的起点为总起点跑单源最短路径了
但还有个问题,怎么确定终点呢?
一旦她到达了一个高度距离 不到 的落脚点,可以直接到墙顶。
很容易理解,墙顶就是我们的终点,但并不是一个横纵坐标都确定的点。那么,我们就可以自己把它建立出来!
所有纵坐标距离最大高度H不到1000的点我们都连上虚拟终点,是不是问题就简单了?
②距离处理
我相信这个其实不用说,你们也知道。
欧几里得距离公式:
③最短路
剩下的就是最短路模板了。
关于跑最短路,我还是秉持我一贯的观点:只要没有负边权,就用Dijkstra+堆优化。
关于SPFA,它死了3.代码实现
我们前面一直只是纸上谈兵,那关键的操作如何实现呢?
很多朋友最短路相关问题,点和边的下标都是从1开始的。那么我们正好可以把0作为虚拟起点,把
f+1作为虚拟终点。也很好理解不是吗?当然也有朋友下标从0开始,其实把
f+1作为虚拟起点,把f+2作为虚拟终点,也完全OK。具体请见代码注释。
#include<cmath> #include<cstdio> #include<queue> #include<algorithm> #include<vector> #define pii pair<int,int>//宏定义,个人习惯 using namespace std; struct Node { int head,dis; double x,y; //横坐标和纵坐标 }node[10005]; struct Edge { int next,to,len; }edge[99990005]; int h,f; //与题目意义相同 bool cmp(Node a,Node b) { if(a.y==b.y)return a.x<b.x; return a.y<b.y; } //排序函数,方便对点进行处理 double calc(double x_1,double y_1,double x_2,double y_2) { return double(sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2))); } //求距离 int cnt; void addEdge(int u,int v,int w) { edge[++cnt].len=w; edge[cnt].next=node[u].head; edge[cnt].to=v; node[u].head=cnt; } //链式前向星存图 void Dijkstra() { for(int i=0;i<=f+1;i++) node[i].dis=0x3f3f3f3f; //别忘了初始化 priority_queue<pii,vector<pii>,greater<pii> >q; //STL小根堆 node[0].dis=0; q.push({0,0}); while(q.size()) { pii tmp=q.top(); q.pop(); int d=tmp.first,u=tmp.second; if(d!=node[u].dis)continue; for(int e=node[u].head;e;e=edge[e].next) { int v=edge[e].to; if(node[v].dis>d+edge[e].len) { node[v].dis=d+edge[e].len; q.push({node[v].dis,v}); } } } } //模板 int main() { scanf("%d%d",&h,&f); for(int i=1;i<=f;i++) { scanf("%lf%lf",&node[i].x,&node[i].y); } sort(node+1,node+f+1,cmp); //排序 for(int i=1;i<=f;i++) { for(int j=i+1;j<=f;j++) { double dist=calc(node[i].x,node[i].y,node[j].x,node[j].y); //求得距离 if(dist<=1000) { //如果两个点间的距离不超过1000, //就可以直接到达 //建立权值为1的边 //表示爬一次 addEdge(i,j,1); addEdge(j,i,1); } } if(h-node[i].y<1000) { //与最大距离H不到1000的点可以直接到达终点 //那么就与虚拟终点连一条边 addEdge(i,f+1,1); addEdge(f+1,i,1); //虚拟终点就是f+1 } if(node[i].y<=1000) { //与地面距离不超过1000的点可以作为起点 //那么就与虚拟起点连一条边 addEdge(0,i,0); addEdge(i,0,0); //虚拟起点就是0 } } Dijkstra(); printf("%d\n",node[f+1].dis); //这里注意输出的是虚拟终点的距离 return 0; }那么,题解到这里就结束了!
写题解不易,希望大家多多支持。如果还有不懂的可以at我或者私信我,我会尽力帮助的!
- 1
信息
- ID
- 5284
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者