标题:方格分割
6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。
如图:p1.png, p2.png, p3.png 就是可行的分割法。
试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法。
请提交该整数,不要填写任何多余的内容或说明文字。
#include<iostream>
using namespace std;
int a[6][6];
int num = 0;
void dfs(int end[][6], int si, int sj,int &cnum,bool state[][6]){
cnum++;
state[si][sj] = true;
if(si-1>=0&&end[si-1][sj] == 1 && !state[si-1][sj])
dfs(end,si-1,sj,cnum,state);
if(si+1<6&&end[si+1][sj] == 1&& !state[si+1][sj])
dfs(end,si+1,sj,cnum,state);
if(sj-1>=0&&end[si][sj-1] == 1&& !state[si][sj-1])
dfs(end,si,sj-1,cnum,state);
if(sj+1<6&&end[si][sj+1] == 1&& !state[si][sj+1])
dfs(end,si,sj+1,cnum,state);
}
void p(){
for(int i =3; i<6; ++i)
for(int j = 0; j<6; ++j)
a[i][j] = !a[5-i][5-j];
int x,y;
for(int i =0; i<6; ++i)
for(int j = 0; j<6; ++j)
{
if(a[i][j] == 1)
{
x = i;
y = j;
}
}
int cnum = 0;
bool state[6][6];
for(int i=0; i<6; ++i)
for(int j = 0; j<6; ++j)
state[i][j] = false;
dfs(a,x, y,cnum,state);
if(cnum == 18)
num++; //遍历确实连通,组合数加一
}
void digui(int position){
if(position == 18)
{
p();//确定了一种状态,开始判断是否连通
return;
}
int i = position/6;
int j = position%6;
a[i][j] = 0;
digui(position+1);
a[i][j] = 1;
digui(position+1);
}
int main()
{
digui(0);
cout<<num/4;//因为有四种重复的旋转对称状态
}
这道题我所用的方法,并不是其他博客所介绍的从中心开始沿着线深搜,当然我也用到了深搜,但含义完全不一样。
方法①时间开销大:
实际上这样的图形都是中心对称图形,我们用0,1来区分不同颜色,那么一定有一半是0,一半是1,而且要保证所有的0或者所有的1都是连通的,这是这道题的图形特征。
那么接下来,我们可能有这样的想法,用18个0,用18个1枚举组合所有的状态,对于每种状态判断①所有的1是否连通,②是否中心对称,这种算法我也写过,但是跑起来非常耗时,可能需要几十分钟,因为是O(n!)复杂度。
方法②时间开销小:
那么我们考虑能不能降低复杂度,我们再次观察图形可以发现,我们只考虑上半部分,对于每一个方格枚举0或者1,这样就只有2^18= 262,144种可能,对于每一种状态,我们都中心对称地补出下半部分,这样我们就能严格保证这是一种中心对称状态,之后再去判断连通性,如果连通就表明是一种组合方法。一两秒就可出结果。上图代码为方法②。