[关闭]
@AkamemakA 2022-07-11T13:46:26.000000Z 字数 1680 阅读 159

Atcoder 题解

Atcoder Beginner Contest 259 problem D 题解


题目链接:点我

题目大意

在平面直角坐标系中给定个圆,第个圆的圆心坐标为,半径为。给定两个坐标,问能否从点通过这些圆的圆周走到点

对于每个测试点,第一行输入,第二行输入,其后行,每行输入

如果能到达,输出"Yes";否则输出"No"。

样例输入1

  1. 4
  2. 0 -2 3 3
  3. 0 0 2
  4. 2 0 2
  5. 2 3 1
  6. -3 3 3

样例输出1

  1. Yes

样例解释1

-

下面是一种从走到的方法:

样例输入2

  1. 3
  2. 0 1 0 3
  3. 0 0 1
  4. 0 0 2
  5. 0 0 3

样例输出2

  1. No

样例解释2

-

这种情况下是不可能达到的。所以输出"No"。

数据范围


解析

乍一看,这个题仿佛没什么思路。

不过仔细思考一下,就可以发现:如果我们将所有圆编号并看做一个点,那么从一个圆的圆周能走到另一个圆的圆周,就是等价于能从这个点走到那个点。

于是,我们双层枚举所有圆,判断两个圆之间是否相交。如果相交,那么两点之间就有一条边。判断完后,就可以一遍简单的深搜找到所在的圆能否到达所在的圆。

起点与终点可能会位于多个圆上,不过这并没有影响。只需要找到其中一个圆即可。


代码实现

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define int long long
  4. const int N=3005;
  5. int n;
  6. int sx,sy,tx,ty;
  7. int x[N],y[N],r[N];
  8. vector<int> v[N]; //邻接矩阵
  9. bool vis[N];
  10. int s,t;
  11. inline bool dfs(int s){
  12. vis[s]=1;
  13. if(s==t) return 1;
  14. for(int i=0;i<v[s].size();i++){
  15. if(vis[v[s][i]])continue;
  16. if(dfs(v[s][i])) return 1;
  17. }
  18. return 0;
  19. }
  20. signed main()
  21. {
  22. cin>>n;
  23. cin>>sx>>sy>>tx>>ty;
  24. for(int i=1;i<=n;i++)
  25. scanf("%lld%lld%lld",&x[i],&y[i],&r[i]);
  26. for(int i=1;i<=n;i++)
  27. for(int j=i+1;j<=n;j++){
  28. int d=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]); //d为两圆心之间的距离 的平方
  29. if(d>(r[i]+r[j])*(r[i]+r[j]) || d<(r[i]-r[j])*(r[i]-r[j])) continue;
  30. //如果圆心距大于两圆半径之和,则两圆相离,这种情况下两圆无法相互到达;
  31. //如果圆心距小于两圆半径之差,则大圆包含着小圆,这种情况下两圆也无法相互到达;
  32. v[i].push_back(j);v[j].push_back(i);
  33. //如果可以相互到达,则点i与点j有双向边。
  34. }
  35. for(int i=1;i<=n;i++){
  36. if((x[i]-sx)*(x[i]-sx)+(y[i]-sy)*(y[i]-sy)==r[i]*r[i]) s=i;
  37. if((x[i]-tx)*(x[i]-tx)+(y[i]-ty)*(y[i]-ty)==r[i]*r[i]) t=i;
  38. }
  39. //找到起点与终点所在圆的编号
  40. if(dfs(s)) cout<<"Yes"; //小dfs一下~
  41. else cout<<"No";
  42. return 0;
  43. }

完结撒花~

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注