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;
}