チーズ〜深さ優先探索のための剪定戦略

タイトル説明

大きなチーズがあります。高さはhhです。長さと幅は無限大と見なすことができます。チーズの中央には同じ半径の球形の空洞がたくさんあります。このチーズに空間座標系を確立することができます。座標系では、チーズの下面はz = 0であり、チーズの上面はz = hです。

さて、チーズの下面に小さなネズミのジェリーがいて、チーズのすべての穴の中心の座標を知っています。2つの空洞が接線または交差している場合、ジェリーは一方の空洞からもう一方の空洞に走ることができます。特に、空洞が下面に接している、または交差している場合、ジェリーはチーズの下面から空洞にぶつかることがあります。空洞の場合は上面に接しているか、上面と交差しており、ジェリーは空洞からチーズの上面まで走ることができます。

チーズの下面にいるジェリーは、既存の空洞を使用して、チーズを破壊せずにチーズの上面まで走ることができるかどうかを知りたいですか?
ここに画像の説明を挿入します

入力フォーマット

ここに画像の説明を挿入します

出力フォーマット

ここに画像の説明を挿入します

入力サンプル

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

サンプル出力

Yes
No
Yes

指示/ヒント

ここに画像の説明を挿入します

データサイズ

ここに画像の説明を挿入します

ACコード

#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1005;
ll n,h,r;
struct xy{
    
    
	ll x,y,z;
}a[maxn];
inline bool cmp(const xy &z1,const xy &z2){
    
    
	return z1.z < z2.z;
}
inline bool isOk(const xy &z1,const xy &z2){
    
    
	if(sqrt((z1.x-z2.x)*(z1.x-z2.x)+(z1.y-z2.y)*(z1.y-z2.y)+(z1.z-z2.z)*(z1.z-z2.z)) <= 2*r){
    
    
		return true;
	}
	return false;
}
bool isAlright = false;
void solve(xy x,ll s,ll g){
    
    
	if(g + r >= h){
    
    
		isAlright = true;
	}
	for(ll i=s;i<=n;i++){
    
    
		if(a[i].z - x.z > 2*r || isAlright)	break;
		else{
    
    
			if(isOk(x,a[i])){
    
    
				solve(a[i],i+1,a[i].z);
			}
		}
	}
}
int main()
{
    
    
	int T;
	cin>>T;
	while(T --){
    
    
		isAlright = false;
		cin>>n>>h>>r;
		for(ll i=1;i<=n;i++){
    
    
			cin>>a[i].x>>a[i].y>>a[i].z;
		}
		sort(a+1,a+n+1,cmp);
		for(int i=1;i<=n;i++){
    
    
			if(a[i].z - r <= 0 && !isAlright){
    
    
				solve(a[i],i+1,a[i].z);
			}
		}
		if(!isAlright){
    
    
			cout<<"No";
		}
		else{
    
    
			cout<<"Yes";
		}
		cout<<endl;
	}
	return 0;
}

全員に2セットのテストデータを送信します

6
2 1000 250
0 0 250
0 0 751
2 1000 400
400 750 5
840 716 718
3 2000 600
328 1454 1005
114 736 1503
860 131 556
4 5000 1000
1249 4828 4646
4638 2770 880
289 267 2924
142 2875 3957
5 1060 300
634 586 1008
756 332 385
570 40 776
428 176 380
695 1028 36
6 8765 4321
2468 2920 3602
613 6875 33
1935 230 809
4916 7853 4995
5960 1560 2989
1758 4793 6410

No
No
No
No
No
Yes
5
1 100 60
70 27 88
1 100 75
80 39 34
1 100 50
605 563 50
1 200 160
120 176 40
1 200 10
50 47 84

No
Yes
Yes
Yes
No

説明

①この質問は複合検索を使用しており、ワイド検索とディープ検索の両方を解決できます。ここではディープ検索を使用します。
②質問を理解することは非常に重要です。質問はジェリーが(0,0,0)から始まることを明確に述べています。つまり、ポイント(0,0,0)を含むことができるボールフィールドがない場合、ジェリーはそれを行うことができません。歩く。、直接出力するだけNo
③詳細検索:

bool isAlright = false;
void solve(xy x,ll s,ll g){
    
    
	if(g + r >= h){
    
    
		isAlright = true;
	}
	for(ll i=s;i<=n;i++){
    
    
		if(a[i].z - x.z > 2*r || isAlright)	break;
		else{
    
    
			if(isOk(x,a[i])){
    
    
				solve(a[i],i+1,a[i].z);
			}
		}
	}
}

正直なところ、このディープサーチのアイデアはユニークではありません.visブール配列の代わりに変数sを使用したことを考えるのは難しいことではなく、入力後のため、このポイントがあったかどうかを判断するステップを省略しました球の高さに応じてボールを整列させました。中心が並べ替えられるため、球の中心に到達するたびに、球の中心座標を格納する配列で、次の球の中心座標のz値(高さ)が必要になります。球の現在の中心よりも高い(または等しい)。深いパスを検索しても終点に到達できない場合は、同じ高さの別の道路から別のパスを見つけることができます)。
プルーニング:プルーニングはこの質問の主要な機能であり、コードは2回プルーニングを使用します。それ以外の場合、後続のすべてのテストはTLEを指します。
剪定は、解決関数に入る前に初めて使用されます。

for(int i=1;i<=n;i++){
    
    
	if(a[i].z - r <= 0 && !isAlright){
    
    //这一步用到了剪枝
		solve(a[i],i+1,a[i].z);
	}
}

このステップ:if(a[i].z - r <= 0 && !isAlright)つまり、球の中心が配置されている球に原点が含まれていない場合はスキップし、抜け道が見つかった場合は判定を終了します。
剪定は、解決関数で2回目に使用されます。

for(ll i=s;i<=n;i++){
    
    
	if(a[i].z - x.z > 2*r || isAlright)	break;//这一步用到了剪枝
		else{
    
    
			if(isOk(x,a[i])){
    
    
				solve(a[i],i+1,a[i].z);
			}
		}
	}

つまりif(a[i].z - x.z > 2*r || isAlright) break;、球の現在の中心が次の球に到達できない場合、ループは終了します。または、抜け道が見つかった場合、ループは終了します。
⑤AC。

おすすめ

転載: blog.csdn.net/fatfairyyy/article/details/113850322