剪邮票
剪邮票
题目描述
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
枚举,检测联通
12选5技巧问题,考虑全排列
package 第七届题目;
import java.util.HashSet;
import java.util.Set;
public class 剪邮票 {
static int ans=0;
static int[] a={0,0,0,0,0,0,0,1,1,1,1,1};
//他的每个排列代表着12选5的一个方案
//这是带有重复元素的排列,会生成很多的重复的排列
//全排列
public 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];//将a[i]填入到path[k]中
f(k+1,path);//递归
vis[i]=false;//回溯
}
}
}
private static boolean check(int path[]) {
// TODO Auto-generated method stub
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){//如果可以填,行号,一行有4个,之后再加上j,对应一维数组的位置
g[i][j]=1;
}else
g[i][j]=0;
}
}
int cnt=0;//连通块的数
//表示g上面就有5个格子为1,之后再用dfs检测连通即可
//二维数组里面的连通性检查是经典问题
//我们每个格子为格子开始,走通调位0如果是0就不同,走通调1,假设还有第2个模块,把所有能连通的都标位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++;//连通块的数目+1
}
}
}
return cnt==1;//判断cnt是否为真,如果连通cnt为真,返回真,如果假,返回假
}
static boolean vis[]=new boolean[12];//标记哪些被访问过,哪些没有被访问过
//检查是否连通
//经典的,用dfs求连通块数目
static void dfs(int[][] g,int i,int j){
g[i][j]=0;//表示直接先开始把他设为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) {
// TODO Auto-generated method stub
int path[]=new int[12];
f(0,path);//从第0位开始
System.out.println(ans);
}
}