说实话,最初看到题目中说的“可以向任意方向移动”吓了一跳,脑中脑补了各种行走的蜿蜒曲径。。。
在一不小心点开标签后才发现,其实只是一个坑点在误导读者。
最短路定义为经过的未被覆盖区域,给定起点和终点,求最短路
首先我们要想清楚一件非常重要的性质,题目中给定的最短路只有一个条件:经过的未被覆盖的路径;
也就是说,为了尽可能的减少暴露的路径的长度,不论覆盖区域中的路径长度有多长,都要走这条路(所以据说输出0都可以得到35分的好成绩)
解决完问题1后,第二个问题迎面而来:最短路怎么跑?
相信一定有不少人这时脑中也脑补了各种弯曲的路线,但我们转念一想:
以下这种情况,哪条路径更优?(蓝色or绿色)
![c射线是什么[ARC064C] Cosmic Rays-宇宙射线 题解_新闻资讯_第1张_活检穿刺产品网 c射线是什么[ARC064C] Cosmic Rays-宇宙射线 题解_https://www.jmylbn.com_新闻资讯_第1张](https://pic.imgdb.cn/item/616063d42ab3f51d9156160f.jpg)
无法判断的话,可以这么理解:
我们以start为圆心,第一段蓝色线段暴露的路径为半径画圆,与黑色的圆相切与A点;
![c射线是什么[ARC064C] Cosmic Rays-宇宙射线 题解_新闻资讯_第2张_活检穿刺产品网 c射线是什么[ARC064C] Cosmic Rays-宇宙射线 题解_https://www.jmylbn.com_新闻资讯_第2张](https://pic.imgdb.cn/item/616067332ab3f51d915e28f4.jpg)
那么第一段蓝色线段一定垂直于切线,而绿色线段与切线有一定的夹角;
所以第一段蓝色线段暴露的路径长度比绿色线段短;
同理,第二段蓝色线段暴露的路径长度也比绿色线段短,所以蓝色路比绿色路优。
至此,我们得到了一条重要的结论:对于一个覆盖的区域来说,经过它的圆心永远比不经过圆心优
其实题目隐藏了一条重要信息:起点和终点也有覆盖范围,只不过是0
那么我们就可以把所有点都归成一类:
struct node{int x,y,len;}a[N];//坐标及半径
瞄一眼数据范围:
这么小?直接点之间两两连边!
点的编号?就是读入顺序,起点和终点另外加两个。
边权?两个点之间的欧几里得距离减去两个点覆盖区域的半径。
怎么跑?dij或spfa皆可,裸的最短路板子。
#include<bits/stdc++.h>
#define N 1005//点数
#define M 1000005//边数
#define inf 0x3f3f3f3f3f3f3f3f//inf开大一点,数据范围是1e9
#define endl '
'
#define debug cerr<<__LINE__<<endl
using namespace std;
int n,m;
int xs,xt,ys,yt;
inline int read()
inline void write(register int x)
inline long long mul(const register int x){return 1ll*x*x;}//一定要记得开long long
inline double get_dis(int x,int y,int a,int b){return sqrt(mul(a-x)+mul(b-y));}//欧几里得距离
namespace first
}
namespace second{
int head[N],cnt;
double dis[N];//欧几里得距离,用double
bool vis[N];
struct edge{int to,nxt;double val;}e[M<<1];//同上,double
struct node{int x,y,len;}a[N];
priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > >q;
inline void add(register int u,register int v,register double w){
e[++cnt].to=v;e[cnt].val=w;e[cnt].nxt=head[u];head[u]=cnt;
e[++cnt].to=u;e[cnt].val=w;e[cnt].nxt=head[v];head[v]=cnt;
}
inline void dijkstra(const register int s)
}
}
}
}
inline double solve(){
for(register int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].len=read();
a[n+1]=(node){xs,ys,0};a[n+2]=(node){xt,yt,0};//额外把起点和终点都当成覆盖区域
for(register int i=1;i<=n+2;i++)
for(register int j=i+1;j<=n+2;j++) add(i,j,max(0.0,get_dis(a[i].x,a[i].y,a[j].x,a[j].y)-a[i].len-a[j].len));
//注意这里要特判dis,如果dis<0就说明两个圆有交集,边权直接赋为0
dijkstra(n+1);return dis[n+2];
}
}
main(void)
码风较奇葩(压行强迫症+卡常小能手)
欢迎觉得做法很麻烦或有错误的巨佬来喷