PAT 1091. Acute Stroke (30) 3重方向并查集(其实非递归DFS也可以,,)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<string.h>
#include<cmath>
#include<string>
using namespace std;


//
/*************************
题意:
给出一个三维立方体,每个点由0或1构成
问由1构成的体积共多少
且构成的每个体积必须大于t
*************************/

/************************
求解要点:
用并查集方法:
	每储存一个1元素,
	查看其left、up、below 3个相邻的点
	(由于储存时从上往下,从西往东,从北往南,故一定可以利用之前的信息)
	若为1,则找到那个点所在集合的父亲(并查集方法)
	
	优先让集合们指向left
	若left不存在则指向up,
	若up不存在指向below
	若都不存在,自身为集合。
	
	同时注意判断重复指向的情况
	即up、below、left的父亲可能是同一个集合
	要加上判断。
************************/

/***********************
笔记:
1.当让3个集合指向1个时,
设定一个优先级
先指a
a没有则指b,b没有则指c
同时进行是否在同一个集合的判断
若相同集合,则不进行相加操作。
*********************/
#define M 1291
#define N 132
#define K 64
#define INF 0xfffff

struct point
{
	int k,i,j;
};

struct father
{
	struct point nextp;
	bool flag;
	int pn;
};

struct father fa[K][M][N];
int a[K][M][N];
int n,m,l;
struct point getf(int k,int i,int j)
{
	point p,pa;
	p.k=k;p.i=i;p.j=j;

	if(a[k][i][j]==0)
	{
		p.k=-1;
		p.i=-1;
		p.j=-1;
		return p;
	}

	if(fa[k][i][j].flag==1)
		return p;

	pa=fa[k][i][j].nextp;
	pa=getf(pa.k,pa.i,pa.j);
	fa[k][i][j].nextp=pa;

	return pa;

}

int main()
{
	int t;
	scanf("%d%d%d%d",&m,&n,&l,&t);
	memset(a,0,sizeof(a));
	struct father f,fk,fi,fj,orf;
	struct point p,pup,pbelow,pleft,pfa;
	int i,j,k;
	
	for(k=0;k<=l+2;k++)
		for(i=0;i<=m+2;i++)
			for(j=0;j<=n+2;j++)
			{
				fa[k][i][j].pn=0;
				fa[k][i][j].flag=0;
			}

	for(k=1;k<=l;k++){
		for(i=1;i<=m;i++)
			for(j=1;j<=n;j++){

				scanf("%d",&a[k][i][j]);
				//printf("%d %d %d\n",k,i,j);
				if(a[k][i][j] == 1){
					p.i=i;
					p.j=j;
					p.k=k;
					fa[k][i][j].pn=1;
					pbelow=getf(k-1,i,j);
					pup=getf(k,i-1,j);
					pleft=getf(k,i,j-1);

					if(a[k][i][j-1]==1)
						pfa=pleft;
					else if(a[k][i-1][j]==1)
						pfa=pup;
					else if(a[k-1][i][j]==1)
						pfa=pbelow;
					else pfa=p;
					
					fa[pfa.k][pfa.i][pfa.j].flag=1;
					fa[pfa.k][pfa.i][pfa.j].nextp=pfa;
					

					
					//若上一行非0,则加上去,但前提是不与pfa相同
					//与pfa相同则代表: 可能为left,可能为up
					if(pup.k!=-1){
						if(pup.k!=pfa.k || pup.i!=pfa.i || pup.j!=pfa.j){
							fa[pup.k][pup.i][pup.j].nextp = pfa;
							fa[pfa.k][pfa.i][pfa.j].pn += fa[pup.k][pup.i][pup.j].pn;
							fa[pup.k][pup.i][pup.j].flag=0;	
						}
					}

					//若上一层非0,则加上去,但前提是不与pfa相同,且不与below相同。
					//即如果要指向left,而below和up父亲相同,则只需要加一次即可
					//与pfa相同则代表: 可能为left,可能为up,可能为below					
					if(pbelow.k!=-1){
						//注意如果below的父亲和up的父亲相同,说明在同一集合
						//则below不可加。
						if(pbelow.k!=pfa.k || pbelow.i!=pfa.i || pbelow.j!=pfa.j){
							if(pbelow.k!=pup.k || pbelow.i!=pup.i || pbelow.j!=pup.j)
							{
								fa[pbelow.k][pbelow.i][pbelow.j].nextp = pfa;
								fa[pfa.k][pfa.i][pfa.j].pn += fa[pbelow.k][pbelow.i][pbelow.j].pn;
								fa[pbelow.k][pbelow.i][pbelow.j].flag=0;
							}
						}
					}

					
					if(k!=pfa.k || i!=pfa.i || j!=pfa.j)
					{
						fa[k][i][j].nextp = pfa;
						fa[pfa.k][pfa.i][pfa.j].pn += 1;
						fa[k][i][j].flag=0;
					}		
	
				}
			}
	}

	int ans=0;
	
	for(k=1;k<=l;k++)
		for(i=1;i<=m;i++)
			for(j=1;j<=n;j++)
			{
				if(fa[k][i][j].flag==1 && fa[k][i][j].pn >= t)
				{
					ans+=fa[k][i][j].pn;
				}
			}

	cout<<ans<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值