【洛谷P4205】智慧珠游戏【dfs】

题目大意:

题目链接:https://www.luogu.org/problem/P4205

智慧珠游戏拼盘由一个三角形盘件和 12 个形态各异的零件组成。拼盘的盘 件如图 1 所示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以放到盘件的任一位置,条件是能有地方放,且 尺寸合适,所有的零件都允许旋转(0º、90º、180º、270º)和翻转(水平、竖直)。

现给出一个盘件的初始布局,求一种可行的智慧珠摆放方案,使所有的零件 都能放进盘件中。


思路:

把每一块智慧珠的所有摆放方式打出来,然后暴搜依次确定位置。
还有什么好说的吗
注意不用手动把所有的情况打出来,只要打出其中一个,让程序自己将这个图形旋转、翻转然后打印进去。注意一个图形是否翻转,以及每次旋转的角度是要打进去的。这样可以将 1000 + 1000+ 1000+行的暴搜压缩成 200 200 200行。
先把难填的图形填进去,方便剪枝。
剪枝就是每到一个局面就泳并查集判断每一个没放智慧珠的空联通快的大小,如果小于3,或者大小是6,就直接返回即可。
最后卡一个搜索次数,达到就直接退了。
时间复杂度 O ( O( O(玄学 ) ) )


代码:

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <ctime>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int tot,flag,pos[13][10][10][3],map[4][4],cpy[4][4],cnt[13],sum[13],vis[15][15],father[200],size[200];
bool used[13];
double T;
char ch;

const int Map[13][4][4]=
{
	{{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
	{{1,1,0,0},{1,0,0,0},{0,0,0,0},{0,0,0,0}},
	{{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
	{{1,1,1,0},{1,0,0,0},{0,0,0,0},{0,0,0,0}},
	{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
	{{1,0,0,0},{1,0,0,0},{1,1,1,0},{0,0,0,0}},
	{{1,1,1,1},{0,1,0,0},{0,0,0,0},{0,0,0,0}},
	{{1,1,1,0},{1,0,1,0},{0,0,0,0},{0,0,0,0}},
	{{1,1,1,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
	{{1,1,1,0},{0,0,1,1},{0,0,0,0},{0,0,0,0}},
	{{0,1,0,0},{1,1,1,0},{0,1,0,0},{0,0,0,0}},
	{{1,0,0,0},{1,1,0,0},{0,1,1,0},{0,0,0,0}},
	{{1,1,1,1},{1,0,0,0},{0,0,0,0},{0,0,0,0}}
};

const int Swap[13]={0,0,0,1,0,0,1,0,1,1,0,0,1};
const int Rotate[13]={0,15,3,15,1,15,15,15,15,15,1,15,15};
const int dx[]={0,0,0,-1,1},dy[]={0,-1,1,0,0};
const int next[]={0,2,4,1,-1,3,12,6,5,11,9,7,8};

inline void add(int S)
{
	for (register int k=1;k<=4;k++)
	{
		for (register int i=0;i<4;i++)
			for (register int j=0;j<4;j++)
				cpy[i][j]=map[3-j][i];
		memcpy(map,cpy,sizeof(map));
		if (Rotate[S]&(1<<k-1))
		{
			int px=-1,py,q=0;
			cnt[S]++;
			for (register int i=0;i<4;i++)
				for (register int j=0;j<4;j++)
					if (map[i][j])
					{
						if (px<0) px=i,py=j;
						else
						{
							q++;
							pos[S][cnt[S]][q][1]=i-px;
							pos[S][cnt[S]][q][2]=j-py;
						}
					}
		}
	}
	if (!Swap[S]) return;
	for (register int i=0;i<4;i++)
		for (register int j=0;j<2;j++)
			swap(map[i][j],map[i][3-j]);
	for (register int k=1;k<=4;k++)
	{
		for (register int i=0;i<4;i++)
			for (register int j=0;j<4;j++)
				cpy[i][j]=map[3-j][i];
		memcpy(map,cpy,sizeof(map));
		if (Rotate[S]&(1<<k-1))
		{
			int px=-1,py,q=0;
			cnt[S]++;
			for (register int i=0;i<4;i++)
				for (register int j=0;j<4;j++)
					if (map[i][j])
					{
						if (px<0) px=i,py=j;
						else
						{
							q++;
							pos[S][cnt[S]][q][1]=i-px;
							pos[S][cnt[S]][q][2]=j-py;
						}
					}
		}
	}
}

inline void prepare()
{
	for (register int i=1;i<=12;i++)
	{
		memcpy(map,Map[i],sizeof(map));
		for (register int j=0;j<4;j++)
			for (register int k=0;k<4;k++)
				sum[i]+=Map[i][j][k];
		add(i);
	}
}

inline bool check(int x,int y,int S,int k)
{
	for (register int i=1;i<=sum[S];i++)
	{
		int xx=x+pos[S][k][i][1],yy=y+pos[S][k][i][2];
		if (vis[xx][yy] || xx>10 || yy>xx || xx<1 || yy<1) return 0;
	}
	return 1;
}

inline int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

inline int com(int x,int y)
{
	return (x-1)*10+y;
}

inline void dfs(int x)
{
	if (x==-1)
	{
		flag=1;
		return;
	}
	tot++;
	for (register int i=1;i<=10;i++)
		for (register int j=1;j<=i;j++)
		{
			father[com(i,j)]=com(i,j);
			size[com(i,j)]=1;
		}
	for (register int i=1;i<=10;i++)
		for (register int j=1;j<=i;j++)
			if (!vis[i][j])
				for (register int k=1;k<=4;k++)
				{
					int xx=i+dx[k],yy=j+dy[k];
					if (!vis[xx][yy])
					{
						if (xx<1 || xx>10 || yy<1 || yy>xx) continue;
						int p=find(com(i,j)),q=find(com(xx,yy));
						if (p==q) continue;
						size[p]+=size[q]; father[q]=p;
					}
				}
	for (register int i=1;i<=10;i++)
		for (register int j=1;j<=i;j++)
			if (!vis[i][j] && size[find(com(i,j))]<3 || size[find(com(i,j))]==6)
				return;
	if (used[x])
	{
		dfs(next[x]);
		return;
	}
	for (register int i=1;i<=10;i++)
		for (register int j=1;j<=i;j++)
			for (int k=1;k<=cnt[x];k++)
				if (check(i,j,x,k))
				{
					for (register int l=1;l<=sum[x];l++)
						vis[i+pos[x][k][l][1]][j+pos[x][k][l][2]]=x;
					dfs(next[x]);
					if (tot>=800000) return;
					if (flag) return;
					for (register int l=1;l<=sum[x];l++)
						vis[i+pos[x][k][l][1]][j+pos[x][k][l][2]]=0;
				}
}

int main()
{
	prepare();
	for (register int i=1;i<=10;i++)
		for (register int j=1;j<=i;j++)
		{
			while (ch=getchar()) if (ch=='.' || (ch>='A' && ch<='Z')) break;
			if (ch!='.')
			{
				used[ch-'A'+1]=1;
				vis[i][j]=ch-'A'+1;
			}
		}
	dfs(10);
	if (flag==1)
		for (register int i=1;i<=10;i++,putchar(10))
			for (register int j=1;j<=i;j++)
				printf("%c",vis[i][j]+'A'-1);
	else printf("No solution\n");
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值