这题作为NOIP2017提高组Day2T1的题目,尽管大家认为这是一道水题,但我感触颇丰…
思路:
这题思路很多,什么DFS啦,BFS啦都可以做。但我还是推荐并查集,有人说并查集会卡常数,但只要你写得好,就不会TLE了…
做题经历:
我刚开始做这题时看了下数据,很显然可以用搜索,于是我手打了一遍DFS,可是交到洛谷评测时得了50分,后面五个点RE,反正可以下测试数据,就下了一下测试点#6,一看:10组数据,每一组1000个洞,我默默地不吱声了…
重新分析算法的我想到了我最近学的并查集,呵呵,应该是可以的,反正有洞连接就合并就好了。于是我就手打了一遍并查集,再次交到洛谷,0分,全WA和RE,我准备重构代码…
后面我重打完代码后再交到洛谷后还以为会得部分分,结果居然AC了,不可思议…
代码及分析:
一个基本的并查集,如果两个洞相连就合并,最后找到底面的洞和顶面的洞判断一遍,在判断之前可以sort一遍,以简化常数。对于两个球而言,如果两个球连通相当于球心距大于半径之和,就可以简化为普通合并…
#include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
class sph{
public:
ll x,y,z;
};
sph hole[100001];
bool has_solution;
ll boss[100001],test,cnt,height,rad;
ll tp,bt,top[1001],bottom[1001];
inline void setup()
{
for(register int i=1;i<=cnt;i++)
{
boss[i]=i;
}
}
inline ll findb(ll node)
{
if(node==boss[node])
{
return node;
}
else
{
boss[node]=findb(boss[node]);
return boss[node];
}
}
inline void Union(ll na,ll nb)
{
ll ba,bb;
ba=findb(na);
bb=findb(nb);
if(ba!=bb)
{
boss[bb]=ba;
}
}
inline bool Find(ll na,ll nb)
{
return findb(na)==findb(nb);
}
inline double dist(sph a,sph b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}
inline bool cmp(sph a,sph b)
{
return a.z<b.z;
}
int main()
{
ios::sync_with_stdio(false);
cin>>test;
for(register int i=0;i<test;i++)
{
cin>>cnt>>height>>rad;
has_solution=0;
memset(boss,0,sizeof(boss));
tp=bt=0;
memset(top,0,sizeof(top));
memset(bottom,0,sizeof(bottom));
setup();
for(register int j=1;j<=cnt;j++)
{
cin>>hole[j].x>>hole[j].y>>hole[j].z;
if(hole[j].z-rad<=0)
{
bottom[bt++]=j;
}
if(hole[j].z+rad>=height)
{
top[tp++]=j;
}
for(register int k=1;k<=j;k++)
{
if(dist(hole[j],hole[k])<=2*rad)
{
Union(j,k);
}
}
}
for(register int j=0;j<bt;j++)
{
for(register int k=0;k<tp;k++)
{
if(Find(bottom[j],top[k]))
{
has_solution=1;
break;
}
}
}
if(has_solution)
{
cout<<"Yes"<<endl;
}
else
{
cout<<"No"<<endl;
}
}
}
总之,这道题还是很水的…