题目描述
6x6的方格,沿着格子的边线剪开成两部分。要求这两部分的形状完全相同。
如图就是三种合法的分割:
试计算:包括这3种分法在内,一共有多少种不同的分割方法。注意:旋转对称的属于同一种分割法。
请提交该整数,不要填写任何多余的内容或说明文字。
思路
这题一开始我是想暴力列举18个点,然后判断,发现时间复杂度太高
然后我又想对格子dfs,发现dfs无法做到那种有分叉的,弯曲的形状,遂放弃
后来想了想:我用剪刀剪,一刀肯定可以完成,而且剪的痕迹要对称,剪到边界,我就完工了啊
恍然大悟:
这题dfs的不是格子,而是dfs剪刀剪切的痕迹
因为一刀可以切完,代表没有分叉的路,剪切的痕迹对称,就是dfs的约束条件,剪切到边界完工,这是dfs的边界条件啊
既然不是对格子dfs,每次我们又只能剪切在格子的边上,那么将方格抽象成剪刀可以经过的点,那么这个点阵的大小是 7x7
如图:剪切的痕迹关于中心点对称
因为剪切的痕迹关于点阵的中心对称
- 我们从点阵的中心出发,向任意方向上的,剪刀未经过的点移动,直到到达点阵的边界
到达边界,说明已经全部被切开了,那么我们找到了一种可能的答案
注意:最后的答案要除4,因为一张正方形的纸,用相同的剪切手法,从四个边入手,得到的形状会是一样的,而我们剪切的时候没有考虑到这个,故会出现中心对称的情况(同一种手法,可以出现4次)
代码
#include <iostream>
using namespace std;
int p[7][7] = {0};
int ans = 0;
bool can(int x, int y)
{
if(0<=x && x<7 && 0<=y && y<7)
if(p[x][y]==0) return true;
return false;
}
void dfs(int x, int y)
{
if(x==0 || x==6 || y==0 || y==6){ans++; return;}
p[x][y]=p[6-x][6-y]=1;
if(can(x-1, y)) dfs(x-1, y);
if(can(x+1, y)) dfs(x+1, y);
if(can(x, y-1)) dfs(x, y-1);
if(can(x, y+1)) dfs(x, y+1);
p[x][y]=p[6-x][6-y]=0;
}
int main()
{
dfs(3, 3);
cout<<ans/4<<endl;
return 0;
}
总结
dfs适合的情况是一条路走到黑,即
- 路径没有分叉
面对一道题目,考虑使用dfs的条件就是:我的答案会有分叉的情况吗?
如果答案中包含有分叉的情况,比如上面分叉的格子也是合法的切割,那么就不能使用dfs,应该在状态没有分叉的问题上使用dfs
因为dfs代表一种“走法”,在dfs 的时候,也要注意,我们的图是否是对称的,得出的答案是否会出现对称的情况,如果对称出现了,那么应该如何去重,都是要考虑的