剪邮票
如【图1.jpg】,有12张连在一起的12生肖的邮票.现在你要从中剪下5张来,要求必须是连着的.(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取.
请你计算,一共有多少种不同的剪取方法.
请填写表示方案数目的整数.
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字.
这道题首先分析问题,要从这34个方格的大小的纸片上裁剪下5块连续的方格
我们可以首先使用全排列将裁剪下5块方格的所有情况排列出来:
将这12个方格放入数组,被裁下来的方格设为1,没被裁下的设为0,然后进行全排列获取到所有的情况
注意这里如果使用交换方式*的全排列是有重复的,并且去重很麻烦,所以这里使用另外一种全排列方法,抓取方式的全排列
交换方式全排列:
for (int i = k; i < 12; i++) {
int t=a[k];
a[k]=a[i];
a[i]=t;
f(k+1);
t=a[k];
a[k]=a[i];
a[i]=t;
}
抓取方式全排列:
可以提前避免重复
for (int i = 0; i < 12;++i{
if(i>0 && a[i] == a[i-1]&&!vis[i-1])continue;
if(!vis[i){//没有被用过的元素可以抓入到path
vis[i]=true;//标记为已访问
path[k]=a[i];//将a[订填入到path[k]中f(k+1,path);//递归
f(k+1,path);
vis[i]=false;//回溯
}
}
题解:
public class test {
static int[] a = {0,0,0,0,0,0,0,1,1,1,1,1};
static boolean[] vis = new boolean[12];
private static int ans=0;
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;
if (!vis[i]){
vis[i]=true;
path[k]=a[i];
f(k+1,path);
vis[i]=false;
}
}
}
private 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;
}
}
int cnt=0;
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;
}
private 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);
}
public static void main(String[] args) {
int[] path = new int[12];
f(0,path);
System.out.println(ans);
}
}