题目:
剪邮票
如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。
图一:
图二、
图三、
给我得收获:
1、抓取法生成全排列。
2、从一维数组映射到二维数组
3、深搜不能解决T字形
解题逻辑:
1、我们知道深搜解决不了T字形,所有深搜不是这题解题主方向。
2、在一维数组中,12个数全排列。前提,去掉重复得排列,留后面麻烦。
3、一位数组映射到二维数组。公式(a[pos] => visit[pos/3][pos%4])。
4、判断visit数组是否连通,如果ans只递归一次就使visit数组全变为0就说明这是连通的、满足条件的,ans++。
代码:生成全排列我用的是 next_permutation(a,a+12) 文末有抓取法生成全排列连接
#include <bits/stdc++.h>
using namespace std;
int a[] = {0,0,0,0,0,0,0,1,1,1,1,1}; //一维数组,当值为1表名选取了该数组下标映射到二维数组所对应的值,比如初始时我在二维数组中选取了8,9,10,11,12这5个格子。
int visit[3][4]; //二维数组
int ans = 0; //总数
int m[] = {1,0,-1,0}; //用来控制方向
int n[] = {0,1,0,-1}; //用来控制方向
//初始化二维数组全为0,映射时才改变内容
void init(){
for(int i = 0;i < 3;i++){
for(int j = 0;j < 4;j++){
visit[i][j] = 0;
}
}
}
//判断是否连通-固定代码
void f(int x,int y){
visit[x][y] = 0;
for(int i = 0;i < 4;i++){
int mx = x + m[i];int ny = y + n[i];
if(mx >= 0 && mx < 3 && ny >= 0 && ny < 4 && visit[mx][ny] == 1){
f(mx,ny);
}
}
}
//从一维数组映射到二维数组 并且判断是否连通
int check(){
for(int i = 0;i < 12;i++){
if(a[i] == 1){
visit[i/4][i%4] = 1; //映射
}
}
int count = 0;
for(int i = 0;i < 3;i++){
for(int j = 0;j < 4;j++){
if(visit[i][j] == 1){
f(i,j); //如果只执行一次递归count就是1,就说明是连通
++count;
}
}
}
return count;
}
int main(){
do{
if(check() == 1){
ans++;
}
}while(next_permutation(a,a+12));
cout << ans << endl;
return 0;
}