剪邮票
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
代码块:
package lqb2016;
public class A7_02 {
//a:数组中的1表示连通的五个邮票
static int[]a = {0,0,0,0,0,0,0,1,1,1,1,1};
//ans:存储不同的剪法
static int ans;
//深搜:判断当前为1的格子的四周是否有1
static void dfs(int[][]g, int i, int j) {
g[i][j] = 0;
if(i-1>=0 && g[i-1][j]==1) dfs(g, i-1, j);
if(i+1<=2 && g[i+1][j]==1) dfs(g, i+1, j);
if(j-1>=0 && g[i][j-1]==1) dfs(g, i, j-1);
if(j+1<=3 && g[i][j+1]==1) dfs(g, i, j+1);
}
//检查抓取的五个邮票是否是连通的
static boolean check(int[]path) {
int[][]g = new int[3][4];
//将某个排列映射到二维矩阵上
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 4; j++) {
if(path[i*4+j] == 1) g[i][j] = 1;
else g[i][j] = 0;
}
}
//cnt:表示连通块的数目
int cnt = 0;
//g上的五个邮票标记为1,用dfs做连通性检查
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 4; j++) {
if(g[i][j] == 1) {
dfs(g, i, j);
cnt++;
}
}
}
return cnt==1;
}
//vis:判断邮票是否被用过
static boolean[]vis = new boolean[12];
static void f(int k, int[]path) {
if(k==12) {
if(check(path)) {
ans++;
}
}
for(int i = 0; i < 12; i++) {
//排重:现在选取的元素和上一个元素相同,但是上个元素没被用过
if(i>0 && a[i]==a[i-1] && !vis[i-1]) continue;
//没有被用过的元素抓入到path中
if(!vis[i]) {
vis[i] = true;//标记已访问
path[k] = a[i];//将a[i]填入到path[K]中
f(k+1, path);//递归
vis[i] = false;//回溯
}
}
}
public static void main(String[]args) {
//原来的数据不动,新建一个容器path,一步一步的装邮票
int[]path = new int[12];
f(0, path);
System.out.println(ans);
}
}
答案:116