SZTUOJ 1007.多连块分解

Description

多连块是指由多个等大正方形边与边连接而成的平面连通图形。
—— 维基百科

给定一个大多连块,你的任务是把它分解成至少两个全等(不能翻转或者旋转)的小多连块。下面的左图是一个合法的分解,右边两幅图不合法。中间那幅图的问题在于其中一个小多连块旋转了,而右图的问题在于其中一个小多连块翻转了。分解出的小多连块数量越少越好。注意:本题一定有解,因为至少可以分解成一大堆单位正方形。

在这里插入图片描述

Input

输入最多包含30组测试数据。每组数据第一行为一个整数n(1<=n<=10)。以下n行描述大多连块,其中每行恰好包含n个字符*或者.,其中*表示属于多连块,.表示不属于。输入保证是合法的多连块。输入多连块至少包含一个正方形,至多包含二十个正方形。输入结束标志为n=0

Output

对于每组数据,输出大多连块的分解方案。每个正方形用一个大写字母表示它所在的小多连块。不同的小多连块应有不同的字母表示。如果有多组解,输出字典序最小的。换句话说,如果我们把输出的n行字符串逐行连接一个长字符串(比如样例2,连接后的字符串为AABB),这个字符串的字典序应该尽量小。每组测试数据的输出后打印一个空行。

Sample Input

5 
..**.
.****
****.
.**..
.....
2
**
**
0

Sample Output

..AA.
.AABB
AABB.
.BB..
.....

AA
BB

Source

湖南省第七届大学生计算机程序设计竞赛

题目解析

继续按顺序来看,这道题其实在整个oj都算比较难的题目了,刚开始不会做,全网都没找到题解,官方标称还看不懂,写了好久才调出来,是一个非常复杂且考验细节的搜索题。建议大家代码能力强了以后再来研究,大致分析一下题目,和上个题类似,只是要给出具体的分解方案,我们的想法是,首先确定分成几部分,一次次递增,最差情况就是每部分都是相同的小方块。然后确定每次检查的块的形状,判断块是否联通,在判断这么多个相同块能否组合成题目要求的图形。具体实现起来还是很复杂的,代码带有注释,大家可以去好好看看代码。

#include<bits/stdc++.h>
using namespace std;
int mp[15][15],mp1[15][15],ans[15][15],n,sum,xi,yi,m,cnt,vis[15][15];
int d[15][15],di[22];
int dir[4][2]={{-1,0},{0,-1},{0,1},{1,0}};
struct P{
	int x,y;
	P(){}
	P(int x,int y):x(x),y(y){}
}pi[22]; 
int move(int x,int y,int cn)//依次填入每个方块,如果非法就返回0; 
{
	int mx=xi-x,my=yi-y;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			if(mp1[i][j])
			{
				if(!mp[i-mx][j-my])return 0;
				ans[i-mx][j-my]=cn;
			}
		}
	return 1;
}
int findv1()
{
	int cn=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		ans[i][j]=mp1[i][j]; 
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(!mp[i][j]||ans[i][j])continue;
			if(!move(i,j,++cn))return 0;//枚举起点和填入方式 
		}
	} 
	 return 1;
} 
void dfs(int d[][15],int x,int y)//判断是否联通
{
	vis[x][y]=1;
	for(int l=0;l<4;l++)
	{
		int i=dir[l][0],j=dir[l][1];
		if(!d[i+x][j+y])continue;
		if(vis[i+x][j+y])continue;
		dfs(d,x+i,y+j);
	}
}
int getmp1(int place,int have)
{
	if(have<=m)//如果还没确定具体的位置就继续确定 
	{
		while(place<sum)
		{
			di[have]=place;//确定哪些位置是放置小方块的地方 
			if(getmp1(place+1,have+1))return 1;//继续下一个 
			if(!place)return 0;
			place++;
		}
		return 0;
	}
	else {
		memset(mp1,0,sizeof(mp1));//清空 
		for(int i=1;i<=m;i++)
		{
			int x=pi[di[i]].x,y=pi[di[i]].y;//根据之前建立的映射di和pi关系,还原出目前的数组 
			mp1[x][y]=1;
		}
		int cot=0;
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=m;i++)
		{
			int x=pi[di[i]].x,y=pi[di[i]].y;//跑一遍判断联通在一个块里面 
			if(vis[x][y])continue;
			cot++;
			dfs(mp1,x,y);
		}
		if(cot!=1)return 0;
		if(findv1())return 1;//检查是否可以吧剩下的拼起来 
		else return 0;
	}
}
int main()
{
	while(scanf("%d",&n)==1&&n)
	{
		char s[15];//临时存储 
		sum=0;
		memset(mp,0,sizeof(mp));
		for(int i=1;i<=n;i++)
		{
			scanf("%s",s);
			for(int j=0;j<n;j++)
			{
				if(s[j]=='*')//存储所有*的位置 
				{
					mp[i][j+1]=1;
					pi[sum]=(P(i,j+1));
					sum++;//统计*的总数 
					if(sum==1)xi=i,yi=j+1;//记录第一个*的位置 
				}
			}
		}
		if(n==1&&mp[1][1]==1){
			printf("A\n\n");
			continue;
		}
		for(int i=2;i<=sum;i++)//枚举一共拆成几部分,最坏情况就是拆成sum个相同的方块; 
		{
			if(sum%i)continue;//有余数表示不可能分成这么多部分 
			m=sum/i;//m代表每部分有几块 
			if(getmp1(0,1))break; 
		} 
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(!ans[i][j])printf(".");
				else printf("%c",'A'+ans[i][j]-1);
			}
			printf("\n"); 
		}
		printf("\n");
	}
	return 0;
}

这个代码量应该算是很小了,题目本来的标称是我这个的2倍还多,而且其实我也没太看懂题目本来给的标称,不过也一起放出来给大家看看。

#include <vector> 
#include <list> 
#include <map> 
#include <set> 
#include <deque> 
#include <queue> 
#include <stack> 
#include <bitset> 
#include <algorithm> 
#include <functional> 
#include <numeric> 
#include <utility> 
#include <sstream> 
#include <iostream> 
#include <iomanip> 
#include <cstdio> 
#include <cmath> 
#include <cstdlib> 
#include <cctype> 
#include <string> 
#include <cstring> 
#include <ctime> 

using namespace std;

char a[15][15];
char a1[15][15];
char best[15][15];
vector<pair<int,int> > p;
int d[100];
int used[100];
int nn,n;

void search2(int x)
{
	int i,j,k,xx,yy,cnt,start,dx,dy;
	vector<int> q;
	if (x==nn)
	{
		memcpy(a1,a,sizeof(a1));
		memset(used,0,sizeof(used));
		q.clear();
		q.push_back(0);
		used[0]=1;
		for (i=0;i<q.size();i++)
		{
			xx=p[d[q[i]]].first;
			yy=p[d[q[i]]].second;
			for (j=0;j<nn;j++)
				if ((used[j]==0)&&(abs(p[d[j]].first-xx)+abs(p[d[j]].second-yy)==1))
				{
					used[j]=1;
					q.push_back(j);
				}
		}
		if (q.size()!=nn) return;
		memset(used,0,sizeof(used));
		for (i=0;i<nn;i++)
		{
			used[d[i]]=1;
			a1[p[d[i]].first][p[d[i]].second]='A';
		}
		cnt=1;
		for (i=0;i<p.size();i++)
			if (used[i]==0)
			{
				used[i]=1;
				a1[p[i].first][p[i].second]='A'+cnt;
				for (j=1;j<nn;j++)
				{
					dx=p[d[j]].first-p[d[0]].first;
					dy=p[d[j]].second-p[d[0]].second;
					xx=p[i].first+dx;
					yy=p[i].second+dy;
					for (k=0;k<p.size();k++)
						if ((used[k]==0)&&(p[k].first==xx)&&(p[k].second==yy))
							break;
					if (k==p.size()) return;
					used[k]=1;
					a1[xx][yy]='A'+cnt;
				}
				cnt++;
			}
		for (i=0;i<n;i++)
			for (j=0;j<n;j++)
			{
				if (best[i][j]<a1[i][j]) return;
				if (best[i][j]>a1[i][j])
				{
					memcpy(best,a1,sizeof(a1));
					return;
				}
			}
		return;
	}
	if (x==0) start=0;
	else start=d[x-1]+1;
	for (i=start;i<p.size();i++)
	{
		d[x]=i;
		search2(x+1);
	}
}

void search1(int x)
{
	int i,j,k,start,dx,dy,xx,yy;
	vector<int> aa;
	vector<int> q;
	if (x==nn)
	{
		memcpy(a1,a,sizeof(a));
		memset(used,0,sizeof(used));
		for (i=0;i<nn;i++)
		{
			used[d[i]]=1;
			a1[p[d[i]].first][p[d[i]].second]='A'+i;
		}
		aa.clear();
		aa.push_back(d[0]);
		for (i=0;i<p.size();i++)
			if (used[i]==0)
			{
				aa.push_back(i);
				used[i]=1;
				a1[p[i].first][p[i].second]='A'+0;
				for (j=1;j<nn;j++)
				{
					dx=p[d[j]].first-p[d[0]].first;
					dy=p[d[j]].second-p[d[0]].second;
					xx=p[i].first+dx;
					yy=p[i].second+dy;
					for (k=0;k<p.size();k++)
						if ((used[k]==0)&&(p[k].first==xx)&&(p[k].second==yy))
						{
							used[k]=1;break;
						}
					if (k==p.size()) return;
					a1[p[k].first][p[k].second]='A'+j;
				}
			}
		memset(used,0,sizeof(used));
		q.clear();
		q.push_back(0);
		used[0]=1;
		for (i=0;i<q.size();i++)
		{
			xx=p[aa[q[i]]].first;
			yy=p[aa[q[i]]].second;
			for (j=0;j<aa.size();j++)
				if ((used[j]==0)&&(abs(p[aa[j]].first-xx)+abs(p[aa[j]].second-yy)==1))
				{
					used[j]=1;
					q.push_back(j);
				}
		}
		if (q.size()!=aa.size()) return;
		for (i=0;i<n;i++)
			for (j=0;j<n;j++)
			{
				if (best[i][j]<a1[i][j]) return;
				if (best[i][j]>a1[i][j])
				{
					memcpy(best,a1,sizeof(a1));
					return;
				}
			}
		return;
	}
	if (x==0) start=0;
	else start=d[x-1]+1;
	for (i=start;i<p.size();i++)
	{
		d[x]=i;
		search1(x+1);
	}
}

int main()
{
	int i,j,cnt;
	double cl = clock();
	
	while (scanf("%d",&n)!=EOF)
	{
		if (n==0) break;
		for (i=0;i<n;i++)
			scanf("%s",a[i]);
		cnt=0;
		p.clear();
		for (i=0;i<n;i++)
			for (j=0;j<n;j++)
				if (a[i][j]=='*')
				{
					p.push_back(make_pair(i,j));
					cnt++;
				}
		memset(best,0,sizeof(best));
		for (i=0;i<n;i++)
			for (j=0;j<n;j++)
				best[i][j]='a';
		for (i=2;i<=cnt;i++)
			if (cnt%i==0)
			{
				j=cnt/i;
				if (i<=j)
				{
					nn=i;
					search1(0);
				}
				else
				{
					nn=j;
					search2(0);
				}
				if (best[0][0]!='a') break;
			}
		for (i=0;i<n;i++)
			printf("%s\n",best[i]);
		printf("\n");
	}
	
	cl = clock() - cl;
	fprintf(stderr, "Total Execution Time = %lf seconds\n", cl / CLOCKS_PER_SEC);
	
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值