1. 程式人生 > >【題解】洛谷P3958[NOIP2017]乳酪 並查集/dfs

【題解】洛谷P3958[NOIP2017]乳酪 並查集/dfs

【題解】 在這裡插入圖片描述 在這裡插入圖片描述 在這裡插入圖片描述 在這裡插入圖片描述

打了一份並查集一份dfs的程式碼

#include<cstdio>
typedef long long ll;
const int N=1e3+10;
int set[N];
ll n,h,r;
struct node{
	ll x,y,z;
}hole[N];//存點 
void init()//初始化代表元全為自己 
{
	for(int i=0;i<=n+1;i++)
	set[i]=i;
}
int findset(int x)//查詢代表元 
{
	if(x==set[x])
	return x;
	else return set[x]=findset(set[x]);
}
void unionset(int x,int y)//並集 
{
	int fx=findset(x);
	int fy=findset(y);
	set[fy]=fx;
}
ll getdis(node a,node b)//返回兩點間的距離的平方 
{
	return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int t,i,j;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld%lld",&n,&h,&r);//此處沒寫lld扣20 
		init();
		int st=0,ed=n+1;//虛擬的起點終點
		for(i=1;i<=n;i++) 
		{
			scanf("%lld%lld%lld",&hole[i].x,&hole[i].y,&hole[i].z);//此處沒寫lld再扣10 
			if(hole[i].z<=r)unionset(i,st);//合併起點和i 
			if(hole[i].z>=h-r)unionset(i,ed);//合併終點和i 
		}
		for(i=1;i<=n;i++)
		for(j=i+1;j<=n;j++)
		if(getdis(hole[i],hole[j])<=(4*r*r))unionset(i,j);//將相通的點合併 
		if(findset(st)==findset(ed))printf("Yes\n");//起點和終點在集合裡說明連通 
		else printf("No\n");
	}
	return 0;
}
//鏈式前向星+dfs 
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 0x7f7f7f7f;
using namespace std;
const int N1=2100000;//不開大點會RE 
const int N2=1100;
typedef long long ll;
ll n,h,r,tot,flag;
//n空洞個數,h高度,r半徑,tot存邊用,flag標記是否有解 
int head[N2],vis[N2];//head存邊用,vis標記節點 
struct node{
	ll x,y,z;
}hole[N2];//長寬高 
queue<int>q;//存起點 
bool e[N2];//標記可行的終點 
ll getdis(node a,node b)//返回距離的平方 
{
	return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
}
struct Edge{
	int to,nextx;
	ll val;
}edge[N1];//鏈式前向星 
void addedge(int u,int v)
{
	edge[tot].to=v;
	edge[tot].nextx=head[u];
	head[u]=tot++;
}//添邊 
void dfs(int u)//搜尋 
{
	if(e[u])//到達終點 
	{
		flag=1;//標記為可行 
		return;
	}
	for(int i=head[u];i!=-1;i=edge[i].nextx)
	{
		int v=edge[i].to;//下個節點 
		if(!vis[v])//沒走過 
		{
			vis[v]=1;//標記 
			dfs(v);//遍歷下個節點 
			if(flag)return;//已經可行不找了 
			//可以不用回溯了沒啥用 
		}
	}
}
int main()
{
	//freopen("in.txt","r",stdin);
	int t,i,j;
	scanf("%d",&t);//測試用例 
	while(t--)
	{
		while(!q.empty())q.pop();
		flag=0;
		tot=0;
		memset(head,-1,sizeof(head));
		memset(vis,0,sizeof(vis));
		memset(e,0,sizeof(e));//以上都是初始化 
		scanf("%lld%lld%lld",&n,&h,&r);
		for(i=1;i<=n;i++)
		{
			scanf("%lld%lld%lld",&hole[i].x,&hole[i].y,&hole[i].z);
			if(hole[i].z<=r)q.push(i);//可以作為起點 
			if(hole[i].z>=h-r)e[i]=1;//可以作為終點 
		}
		for(i=1;i<=n;i++)
		for(j=1;j<i;j++)
		if(getdis(hole[i],hole[j])<=4*r*r)//兩點連通 
		{
			addedge(i,j);
			addedge(j,i);
		}
		while(!q.empty())
		{
			int st=q.front();
			q.pop();
			dfs(st);//從起點開始遍歷 
			if(flag)break;
		}
		if(flag)
		printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

總結

好久以前寫的