@AkamemakA
2022-07-11T13:46:26.000000Z
字数 1680
阅读 159
Atcoder
题解
题目链接:点我
在平面直角坐标系中给定个圆,第个圆的圆心坐标为,半径为。给定两个坐标,问能否从点通过这些圆的圆周走到点。
对于每个测试点,第一行输入,第二行输入,其后行,每行输入。
如果能到达,输出"Yes";否则输出"No"。
4
0 -2 3 3
0 0 2
2 0 2
2 3 1
-3 3 3
Yes
下面是一种从走到的方法:
3
0 1 0 3
0 0 1
0 0 2
0 0 3
No
这种情况下是不可能达到的。所以输出"No"。
乍一看,这个题仿佛没什么思路。
不过仔细思考一下,就可以发现:如果我们将所有圆编号并看做一个点,那么从一个圆的圆周能走到另一个圆的圆周,就是等价于能从这个点走到那个点。
于是,我们双层枚举所有圆,判断两个圆之间是否相交。如果相交,那么两点之间就有一条边。判断完后,就可以一遍简单的深搜找到所在的圆能否到达所在的圆。
起点与终点可能会位于多个圆上,不过这并没有影响。只需要找到其中一个圆即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3005;
int n;
int sx,sy,tx,ty;
int x[N],y[N],r[N];
vector<int> v[N]; //邻接矩阵
bool vis[N];
int s,t;
inline bool dfs(int s){
vis[s]=1;
if(s==t) return 1;
for(int i=0;i<v[s].size();i++){
if(vis[v[s][i]])continue;
if(dfs(v[s][i])) return 1;
}
return 0;
}
signed main()
{
cin>>n;
cin>>sx>>sy>>tx>>ty;
for(int i=1;i<=n;i++)
scanf("%lld%lld%lld",&x[i],&y[i],&r[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++){
int d=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); //d为两圆心之间的距离 的平方
if(d>(r[i]+r[j])*(r[i]+r[j]) || d<(r[i]-r[j])*(r[i]-r[j])) continue;
//如果圆心距大于两圆半径之和,则两圆相离,这种情况下两圆无法相互到达;
//如果圆心距小于两圆半径之差,则大圆包含着小圆,这种情况下两圆也无法相互到达;
v[i].push_back(j);v[j].push_back(i);
//如果可以相互到达,则点i与点j有双向边。
}
for(int i=1;i<=n;i++){
if((x[i]-sx)*(x[i]-sx)+(y[i]-sy)*(y[i]-sy)==r[i]*r[i]) s=i;
if((x[i]-tx)*(x[i]-tx)+(y[i]-ty)*(y[i]-ty)==r[i]*r[i]) t=i;
}
//找到起点与终点所在圆的编号
if(dfs(s)) cout<<"Yes"; //小dfs一下~
else cout<<"No";
return 0;
}
完结撒花~