[蓝桥杯2016初赛]剪邮票
题目描述
如下图, 有12张连在一起的12生肖的邮票。现在你要从中剪下5张来,要求必须是连着的。(仅仅连接一个角不算相连)
比如,下面两张图中,粉红色所示部分就是合格的剪取。
请你计算,一共有多少种不同的剪取方法。
输出
请填写表示方案数目的整数。
package pro;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
public class Main {
static int[] arr = {1,2,3,4,5,6,7,8,9,10,11,12};
static int[][] num = {{0,1},{0,-1},{1,0},{-1,0}};
static int[] vis = new int[12];
static int[] res2 = new int[5];
static Map<String,Integer> map = new HashMap<String,Integer>();
static int cnt = 0,n;
public static void main(String[] args) {
dps(0,0);
System.out.println(cnt);
}
static void dps(int i,int m) {
if(i==5) {
if(test()) {
/*int[] array = new int[5];
for(int j = 0; j < 5; j++)
array[j] = res2[j];
Arrays.sort(array);
String s = "";
for(int a:array)
s += String.valueOf(a);
if(map.containsKey(s)==false) {
map.put(s, 1);
cnt++;
}*/
cnt++;
}
}else {
for(int k = 0; k < 12; k++) {
if(vis[k]==0) {
if(m>arr[k]) //剪枝
continue;
res2[i] = arr[k];
vis[k] = 1;
dps(i+1,arr[k]);
vis[k] = 0;
}
}
}
}
static boolean test() {
int x=0,y=0;
int[][] arr = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int[][] res = new int[3][4];
int[][] vis2 = new int[3][4];
for(int k = 0; k < 5; k++) {
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 4; j++) {
if(arr[i][j]==res2[k]) {
if(k==0) {
x = i;
y = j;
}
res[i][j] = 1;
}
}
}
}
int count = 1;
Queue<Point> queue = new LinkedList<Point>();
Point p1 = new Point(x,y);
vis2[x][y] = 1;
queue.add(p1);
while(!queue.isEmpty()) {
Point p = queue.poll();
for(int i = 0; i < 4; i++) {
int a = p.x + num[i][0];
int b = p.y + num[i][1];
if(a>=0&&a<3&&b>=0&&b<4&&vis2[a][b]==0&&res[a][b]!=0) {
count++;
queue.add(new Point(a,b));
vis2[a][b] = 1;
}
}
}
if(count==5)
return true;
return false;
}
}
class Point{
int x,y;
public Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
注:全排列+连通性判断
连通性判断方法:
1.并查集 并查集介绍
2.dfs
3.bfs
去重和连通性判断较麻烦
剪枝之后无需判重
2021-01-30