[今天typora好像和博客园过不去,代码一直无法正常显示,想要更好的观感请移步file:///C:/Users/THTF/Desktop/noip/NOIP2017%E5%A5%B6%E9%85%AA%EF%BC%88%E5%B9%B6%E6%9F%A5%E9%9B%86%EF%BC%8C%E6%90%9C%E7%B4%A2%EF%BC%89.html]
NOIP2017奶酪(并查集,搜索)
题目描述
现有一块大奶酪,它的高度为 h,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系, 在坐标系中,奶酪的下表面为 z = 0,奶酪的上表面为 z = h。
现在, 奶酪的下表面有一只小老鼠 Jerry, 它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交, Jerry 则可以从奶酪下表面跑进空洞; 如果一个空洞与上表面相切或是相交, Jerry 则可以从空洞跑到奶酪上表面。
位于奶酪下表面的 Jerry 想知道, 在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去?
空间内两点 P1(x1,y1,z1) 、P2(x2,y2,z2) 的距离公式如下: d = $sqrt{(x_1-x_2)^2 + (y_1-y_2)^2 +(z_1- z_2)^2}$
现在, 奶酪的下表面有一只小老鼠 Jerry, 它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交, Jerry 则可以从奶酪下表面跑进空洞; 如果一个空洞与上表面相切或是相交, Jerry 则可以从空洞跑到奶酪上表面。
位于奶酪下表面的 Jerry 想知道, 在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去?
空间内两点 P1(x1,y1,z1) 、P2(x2,y2,z2) 的距离公式如下: d = $sqrt{(x_1-x_2)^2 + (y_1-y_2)^2 +(z_1- z_2)^2}$
输入描述:
每个输入文件包含多组数据。
输入文件的第一行,包含一个正整数 T,代表该输入文件中所含的数据组数。
接下来是 T 组数据,每组数据的格式如下:
第一行包含三个正整数 n, h 和 r, 两个数之间以一个空格分开,分别代表奶酪中空洞的数量,奶酪的高度和空洞的半径。
接下来的 n 行,每行包含三个整数 x, y, z, 两个数之间以一个空格分开, 表示空洞球心坐标为 (x,y,z)。
输出描述:
输出文件包含 T 行,分别对应 T 组数据的答案,如果在第 i 组数据中, Jerry 能从下表面跑到上表面,
则输出“Yes”,如果不能,则输出“No”(均不包含引号)。
备注:
对于 20%的数据, n = 1, 1 ≤ h , r ≤ 10,000,坐标的绝对值不超过 10,000。
对于 40%的数据, 1 ≤ n ≤ 8, 1 ≤ h , r ≤ 10,000,坐标的绝对值不超过 10,000。
对于 80%的数据,1 ≤ n ≤ 1,000, 1 ≤ h , r ≤ 10,000,坐标的绝对值不超过 10,000。
对于 100%的数据, 1 ≤ n ≤ 1,000, 1 ≤ h , r ≤ 1,000,000,000, T ≤ 20,坐标的绝对值不超过 1,000,000,000。
解题报告
首先检讨 考试心态 太不正式 (本来做出题来就不容易竟然还不好好对待 !罪悪は忍び難い!)
对于题目,想到了两种做法(当然都实现挂了只有20分)
现在分别记录下来
1.搜索
- 思路都是差不多的,关键应该是实现洞与洞之间的连接。
- yxt大佬的提示:可能环状走
- 不知道离散化+分层效果会怎么样,有时间搞搞
下面上大哥 精炼无比精妙绝伦异彩纷呈 的代码%%%
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1509;
int T,n;
ll h,r;
struct node{
ll x,y,z;
}a[N];
vector<int> to[N];
bool ok[N],vis[N],ans;
void dfs(int u){
//0肯定不通顶
if(ok[u]) {
ans=1;
return ;
}
for(unsigned int i=0;i<to[u].size();i++)
if(!ans){
int v=to[u][i];
if(vis[v]) continue;
vis[v]=1;
dfs(v);
}
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%lld%lld",&n,&h,&r);
for(int i = 0; i <= n; i++) to[i].clear();
for(int i = 1; i <= n; i++){
scanf("%lld%lld%lld", &a[i].x, &a[i].y, &a[i].z);
if(a[i].z - r <= 0) to[0].push_back(i); //通底
if(a[i].z + r >= h) ok[i] = 1; //通顶
else ok[i] = 0;
}
ll d=r*r*4;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i!=j){
ll dis=(a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)+(a[i].z-a[j].z)*(a[i].z-a[j].z);
if(dis<=d) to[i].push_back(j); //看!大哥思维巧妙的地方就在这儿了:人家存的是可以联通的那些鸭qwq
}
ans = 0;
memset(vis,0,sizeof vis);
dfs(0);
if(ans) printf("Yes
");
else printf("No
");
}
}
下面是蒟蒻错了的20分代码
嗯……题目说从下往上跑,窝没看见,直接从上往下搜了……QAQ不仔细读题gg啊……水样例害人不浅啊……
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fr(i,n) for(int i = 1; i <= n; i++)
const int N = 1500;
ll t,n,jk[N],bj,len,js;
ll h,a[N],b[N],c[N];
double r;
struct node{
ll x,y,z;
}yo[N];
int cmp(node a, node b){
if(a.z == b.z) return a.x > b.x;
else return a.z > b.z;
}
void dfs(int i){
js = 0;
if(yo[i].z - r <= 0){
cout << "Yes" << endl;
js = 1;
return ;
}
for(int z = 1; z <= len; z++)
for(int j = len + 1; j <= n; j++){
double disf = pow(abs(yo[z].x - yo[j].x),2) + pow(abs(yo[z].y - yo[j].y),2) + pow(abs(yo[z].z - yo[j].z),2);
if(disf <= 4*r*r) dfs(j);
}
}
int main(){
ios::sync_with_stdio(false);
cin >> t;
while(t--){
bj = 0, len = 0;
cin >> n >> h >> r;
fr(i,n) {
cin >> yo[i].x >> yo[i].y >> yo[i].z;
if(yo[i].z + r >= h) bj = 1, len++;
}
if(!bj) {
cout << "No" << endl;
continue;
}
sort(yo + 1, yo + 1 + n, cmp);
dfs(1);
if(!js) cout << "No"<<endl;
}
return 0;
}
并查集
其实和上面那个搜索没多大区别……
//我准备搞个并查集emmmm
//对看了牛客的标签才开始想并查集的(
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1500;
ll f[N], a[N], b[N], c[N], n, h, r, t;
ll find(ll k){ return f[k] == k?k:f[k] = find(f[k]); }
void add(int x,int y){ f[find(y)] = find(x); }
double dis(ll x,ll y,ll z,ll a,ll b,ll c){ return sqrt((x-a)*(x-a)+(y-b)*(y-b)+(z-c)*(z-c)); } //距离
int main()
{
ios::sync_with_stdio(false);
cin >> t;
while(t--){
cin >> n >> h >> r;
for(int i = 0; i <= n + 1; i++) f[i] = i;
for(int i = 1; i <= n; i++){
cin >> a[i] >> b[i] >> c[i];
if(c[i] + r >= h) add(i,n+1);
if(c[i] - r <= 0) add(i,0);
}
for(int i = 1; i <= n - 1; i++)
for(int j = i + 1; j <= n; j++)
if(dis(a[i], b[i], c[i], a[j], b[j], c[j]) <= 2 * r) add(i,j);
if(find(0) == find(n+1)) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}