蓝桥杯第七届真题 :剪邮票

点击查看:蓝桥杯历年真题 题解目录

剪邮票

蓝桥杯第七届真题 :剪邮票_第1张图片

答案: 116
思路分析:
1. 因为dfs只能一条道走到黑,不能产生"T"字型的邮票
2. 即在12个方格中选出5个方格,判断是否能连成一块,即判断连通块的个数
    如果有1个连通块 说明5张邮票相邻,ans++;
3. 对 a[] = {0,0,0,0,0,0,0,1,1,1,1,1};进行全排列,用Set集合去重,以保证不重复计数
4. 全排列的模板很简单,代码中已注释
5. 将一维数组a 转为二维数组 t ,数组中的每个元素代表该邮票是否被选中(1为选中,0未选中)
6. 然后转化为求解连通块的问题及模板
7. if(连通块的个数==1) ans++;
8. 因为涉及到 12个元素的全排列,程序运行耗时约 1 min,但这是结果填空题,so问题不大
package java_B_2016;
import java.util.Set;
import java.util.TreeSet;
public class Main007_剪邮票 {
   static int size;
   static int a[] = {0,0,0,0,0,0,0,1,1,1,1,1};
   static int t[][] = new int[3][4];
   static int ans;
   static Set<String> set = new TreeSet<String>();
   public static void main(String[] args) {
      f(0);
      System.out.println(ans);
   }
   private static void f(int k) {
      if(k==12) {
         StringBuffer sb = new StringBuffer();
         for(int i=0;i<12;i++) {
            sb.append(a[i]);
         }
         set.add(sb.toString());
         if(set.size()>size) {
            size=set.size();
            // 执行语句
            for(int i=0;i<3;i++){
               for(int j=0;j<4;j++) {
                  t[i][j]=a[4*i+j];
               }
            }
            int count=0; // count记录连通块的个数,即5张小邮票是否相邻,count=1则5个方块相邻
            // 已经生成相应的含5个元素的数组,转化为求连通块的问题
            for(int i=0;i<3;i++)
               for(int j=0;j<4;j++) 
                  if(t[i][j]==1) {
                     dfs(i,j);
                     count++;
                  }
            if(count==1) ans++;
         }
      }else {
         // 对数组a的数据进行全排列
         for(int i=k;i<12;i++) {
            int t = a[i]; a[i] = a[k]; a[k] = t;
            f(k+1);
            t = a[i]; a[i] = a[k]; a[k] = t;
         }
      }
   }
   private static void dfs(int i, int j) {
      t[i][j]=0;
      if(i+1<=2&&t[i+1][j]==1) dfs(i+1, j);
      if(i-1>=0&&t[i-1][j]==1) dfs(i-1, j);
      if(j+1<=3&&t[i][j+1]==1) dfs(i, j+1);
      if(j-1>=0&&t[i][j-1]==1) dfs(i, j-1);
   }
}

在这里插入图片描述

下面这个方法可以秒出结果
public class Main007_剪邮票2 {
    static int a[] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};//它的每个排列代表着12选5的一个方案
    static int ans;

    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;
          }
       }
       int cnt = 0;//连通块的数目
       //  g上面就有5个格子被标记为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;
    }
    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;//现在准备选取的元素和上一个元素相同,但是上一个元素还没被使用
          if (!vis[i]) {//没有被用过的元素可以抓入到path
              vis[i] = true;//标记为已访问
              path[k] = a[i];//将a[i]填入到path[k]中
              f(k + 1, path);//递归
              vis[i] = false;//回溯
           }
       }
    }
    public static void main(String[] args) {
       int path[]=new int[12];
       f(0,path);
       System.out.println(ans);
   }
}

你可能感兴趣的:(蓝桥杯历届真题)