zoukankan      html  css  js  c++  java
  • 第四届蓝桥杯JavaC组省赛真题

    解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论

    题目1、猜年龄

    题目描述
    美国数学家维纳(N.Wiener)智力早熟,11岁就上了大学。他曾在1935~1936年应邀来中国清华大学讲学。

    一次,他参加某个重要会议,年轻的脸孔引人注目。于是有人询问他的年龄,他回答说:
    
    “我年龄的立方是个4位数。我年龄的4次方是个6位数。这10个数字正好包含了从0到9这10个数字,每个都恰好出现1次。”
    
    请你推算一下,他当时到底有多年轻。
    
    通过浏览器,直接提交他那时的年龄数字。
    注意:不要提交解答过程,或其它的说明文字。
    
    public class Main {
    
      public static void main(String[] args) {
        for (int i = 1; i < 100; i++) {
          for (int j = 1; j < 100; j++) {
            if ((i+j)*6==i*j && Math.abs(i-j)<=8 && i!=j) {
              System.out.println(i+" "+j);
            }
          }
        }
    
      }
    
    }
    
    题目2、组素数

    题目描述
    素数就是不能再进行等分的数。比如:2 3 5 7 11 等。
    9 = 3 * 3 说明它可以3等分,因而不是素数。

    我们国家在1949年建国。如果只给你 1 9 4 9 这4个数字卡片,可以随意摆放它们的先后顺序(但卡片不能倒着摆放啊,我们不是在脑筋急转弯!),那么,你能组成多少个4位的素数呢?
    
    比如:1949,4919 都符合要求。
    

    请你提交:能组成的4位素数的个数,不要罗列这些素数!!

    注意:不要提交解答过程,或其它的辅助说明文字。

    //处理从k开始的排列
    static void f(int [] arr,int k){
    	if(k==4)//前面都已确定
    		check(arr);
    	for(int i=k;i<4;i++){
    		//交换
    		int t = arr[k];
    		arr[k] = arr[i];
    		arr[i] = t;
    		
    		f(arr,k+1);
    		
    		t = arr[k];
    		arr[k] = arr[i];
    		arr[i] = t;
    	}
    }
    static Set<Integer> set = new HashSet<Integer>();
    private static void check(int [] arr){
    	int x = arr[0]*1000+arr[1]*100+arr[2]*10+arr[3];
    	boolean flag = true;
    	for(int i=2;i<=Math.sqrt(x);i++){
    		if(x%i==0){
    			flag=false;
    			break;
    		}
    		if(flag)
    			set.add(x);
    	}
    }
    public static void main(String[] args) {
    	int arr[] = {1,9,4,9};
    	f(arr,0);
    	System.out.println(set.size());
    	}
    
    
    题目3、马虎的算式

    题目描述
    小明是个急性子,上小学的时候经常把老师写在黑板上的题目抄错了。

    有一次,老师出的题目是:36 x 495 = ?

    他却给抄成了:396 x 45 = ?

    但结果却很戏剧性,他的答案竟然是对的!!

    因为 36 * 495 = 396 * 45 = 17820

    类似这样的巧合情况可能还有很多,比如:27 * 594 = 297 * 54

    假设 a b c d e 代表1~9不同的5个数字(注意是各不相同的数字,且不含0)

    能满足形如: ab * cde = adb * ce 这样的算式一共有多少种呢?

    请你利用计算机的优势寻找所有的可能,并回答不同算式的种类数。

    满足乘法交换律的算式计为不同的种类,所以答案肯定是个偶数。

    答案直接通过浏览器提交。
    注意:只提交一个表示最终统计种类数的数字,不要提交解答过程或其它多余的内容。

    public class Main {
    
      public static void main(String[] args) {
        int count=0;
        for (int a = 1; a < 10; a++) {
          for (int b = 1; b < 10; b++) {
            for (int c = 1; c < 10; c++) {
              for (int d = 1; d < 10; d++) {
                for (int e = 1; e < 10; e++) {
                  int arr[] = new int[]{a,b,c,d,e};
                  if (f(arr)==1 && (a*10+b)*(c*100+d*10+e)==(a*100+d*10+b)*(c*10+e)) {
                    System.out.println((a*10+b)+"*"+(c*100+d*10+e)+"="+(a*100+d*10+b)+"*"+(c*10+e)+"="+(a*10+b)*(c*100+d*10+e));
                    count++;
                  }
                }
              }
            }
          }
        }
        System.out.println(count);
      }
    
      public static int f(int arr[]){
        for (int i = 0; i < arr.length-1; i++) {
          for (int j = i+1; j < arr.length; j++) {
            if (arr[i]==arr[j]) {
              return 0;
            }
          }
        }
      return 1;
      }
    
    }
    
    题目4、阶乘位数

    题目描述
    小明刚刚看完电影《第39级台阶》,离开电影院的时候,他数了数礼堂前的台阶数,恰好是39级!

    站在台阶前,他突然又想着一个问题:
    
    如果我每一步只能迈上1个或2个台阶。先迈左脚,然后左右交替,最后一步是迈右脚,也就是说一共要走偶数步。那么,上完39级台阶,有多少种不同的上法呢?
    
    
    请你利用计算机的优势,帮助小明寻找答案。
    

    要求提交的是一个整数。
    注意:不要提交解答过程,或其它的辅助说明文字。

    public class Main {
    public static void main(String[] args) {
    	
    	boolean a = false;
    	System.out.println(F(39,a));
    	
    }
    public static int F(int n,boolean mark) {
    	if(n==0&&mark==false) {
    		return 1;
    	}
    	if(n<=0) {
    		return 0;
    	}
    	return F(n-1,!mark)+F(n-2,!mark);
    }
    }
    
    题目5、有理数类
    题目描述
     有理数就是可以表示为两个整数的比值的数字。一般情况下,我们用近似的小数表示。但有些时候,不允许出现误差,必须用两个整数来表示一个有理数。
    
     这时,我们可以建立一个“有理数类”,下面的代码初步实现了这个目标。为了简明,它只提供了加法和乘法运算。
    
        public static void main(String[] args) {
            Rational a = new Rational(1,3);
            Rational b = new Rational(1,6);
            Rational c = a.add(b);
            System.out.println(a + "+" + b + "=" + c);
        }
    
        private static class Rational
        {
            private long ra;
            private long rb;
    
            //辗转相除法求最大公约数gcd
            private long gcd(long a, long b){
                if(b==0) return a;
                return gcd(b,a%b);
            }
            public Rational(long a, long b){
                ra = a;
                rb = b; 
                long k = gcd(ra,rb);
                if(k>1){ //需要约分
                    ra /= k;  
                    rb /= k;
                }
            }
            // 加法
            public Rational add(Rational x){
                return new Rational(ra*x.rb+x.ra*rb, rb*x.rb);  //填空位置
            }
            // 乘法
            public Rational mul(Rational x){
                return new Rational(ra*x.ra, rb*x.rb);
            }
            public String toString(){
                if(rb==1) return "" + ra;
                return ra + "/" + rb;
            }
        }
    
    使用该类的示例:
        Rational a = new Rational(1,3);
        Rational b = new Rational(1,6);
        Rational c = a.add(b);
        System.out.println(a + "+" + b + "=" + c);
    
    
    请分析代码逻辑,并推测划线处的代码,通过网页提交
    注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!
    
    
    
    题目6、逆波兰表达式
    正常的表达式称为中缀表达式,运算符在中间,主要是给人阅读的,机器求解并不方便。 
    例如:3 + 5 * (2 + 6) - 1 
    而且,常常需要用括号来改变运算次序。 
    相反,如果使用逆波兰表达式(前缀表达式)表示,上面的算式则表示为: 
    - + 3 * 5 + 2 6 1 
    不再需要括号,机器可以用递归的方法很方便地求解。 
    为了简便,我们假设: 
    1. 只有 + - * 三种运算符 
    2. 每个运算数都是一个小于10的非负整数 
    下面的程序对一个逆波兰表示串进行求值。 
    其返回值为一个数组:其中第一元素表示求值结果,第二个元素表示它已解析的字符数。 请分析代码逻辑,并推测划线处的代码,通过网页提交。 
    注意:仅把缺少的代码作为答案,千万不要填写多余的代码、符号或说明文字!!
    
    【分析】 
    解决此题必须抽象看问题 
    首先分析出{x,y} 数组两个元素,前一个表示算出来的数值,后一个表示处理了几个位置的数字 
    -+3*5+261 —> - (+3*5+26) 1 
    (+3*5+26) 返回来的数组应该是{43,7} 
    如何得到后面的1呢? 
    一定要使用这里面的7,跳过这7个数去求结果数组 
    所以答案是:evaluate(x.substring(1+v1[1]))
    
    
        public static void main(String[] args) {
            //逆波兰表达式字符串
            String s = "-+3*5+261";
            //得到计算结果数组
            int[] result = evaluate(s);
            //打印结果
            System.out.println(result[0]);
        }
    
        //计算逆波兰的递归函数
        static int[] evaluate(String x){
            if(x.length()==0) 
                return new int[] {0,0};
    
            char c = x.charAt(0);
            if(c>='0' && c<='9') 
                return new int[] {c-'0',1};
    
            int[] v1 = evaluate(x.substring(1));
            int[] v2 = evaluate(x.substring(1+v1[1]));  //填空位置
    
            int v = Integer.MAX_VALUE;
            if(c=='+') v = v1[0] + v2[0];
            if(c=='*') v = v1[0] * v2[0];
            if(c=='-') v = v1[0] - v2[0];
    
            return new int[] {v,1+v1[1]+v2[1]};
        }
    
    题目7、核桃的数量

    题目描述
    小张是软件项目经理,他带领3个开发组。工期紧,今天都在加班呢。为鼓舞士气,小张打算给每个组发一袋核桃(据传言能补脑)。他的要求是:

    1. 各组的核桃数量必须相同
    2. 各组内必须能平分核桃(当然是不能打碎的)
    3. 尽量提供满足1,2条件的最小数量(节约闹革命嘛)

    程序从标准输入读入:
    a b c
    a,b,c都是正整数,表示每个组正在加班的人数,用空格分开(a,b,c<30)

    程序输出:
    一个正整数,表示每袋核桃的数量。

    例如:
    用户输入:
    2 4 5

    程序输出:
    20

    再例如:
    用户输入:
    3 1 1

    程序输出:
    3

    资源约定:
    峰值内存消耗(含虚拟机) < 64M
    CPU消耗 < 1000ms

    请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
    注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
    注意:主类的名字必须是:Main,否则按无效代码处理。

    import java.util.Scanner;
     
    public class Main {
    	public static void main(String args[]){
    		Scanner cin = new Scanner(System.in);
    		int a =cin.nextInt(),b=cin.nextInt(),c=cin.nextInt();
    		int max;
    		if(a<b)max=b;else max=a;
    		if(max<c)max=c;
    		for(int i=max ; i<=a*b*c;i++)
    			if(i%a==0&&i%b==0&&i%c==0){max=i;break;}
    		System.out.println(max);
    	}
    		
    }
    
    题目8、打印十字图

    题目描述
    小明为某机构设计了一个十字型的徽标(并非红十字会啊),如下所示(可参见p1.jpg)

    对方同时也需要在电脑dos窗口中以字符的形式输出该标志,并能任意控制层数。
    
    为了能准确比对空白的数量,程序要求对行中的空白以句点(.)代替。
    

    输入格式:
    一个正整数 n (n<30) 表示要求打印图形的层数

    输出:
    对应包围层数的该标志。

    例如:
    用户输入:
    1
    程序应该输出:

    再例如:
    用户输入:
    3
    程序应该输出:

    请仔细观察样例,尤其要注意句点的数量和输出位置。

    资源约定:
    峰值内存消耗(含虚拟机) < 64M
    CPU消耗 < 1000ms

    请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
    注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。

    在这里插入图片描述
    问题分析:
    当我看到这个题目的时候,密密麻麻的完全看不出来哪里有个十字嘛。将上面的文字符号图像形象化一下,如下图:

    这张图,中间是个十字,外面是一层一层的圈。我们再加几条线,就能更清晰一些了。如下图:

    我们将图片左上角分成3个区域。好了,接下来说说我们的打印思路:

    思路:
    (我们只打印 ’ $ ',不打印 ’ . ’ )
    第一步:将整个图片全部初始化为白色格子(我们只打印红色格子)。

    第二步:打印中心十字(也可以只打印左上角部分的十字)。

    第三步:打印A区域,从图片的中心点开始(注意规律:1,3,5,7个数)。

    第四步:打印C区域,同样从中心开始打印(注意规律)

    第五步:沿着对角线打印B区域。(OK现在基本上打印完毕了)

    第六步:根据对称性,我们将图片(左右对折,上下对折,沿着对角线对折),OK图片打印完毕。

    ps:这里有个需要注意的地方,就是打印的时候,不要加空格,你看它的图形是长方形的,如果你加上了空格就变成了正方形的了。

    import java.util.Scanner;
    public class dayinshizitu {
    	  public static void getResult(int n) {
    	        int len = 5 + 4 * n;
    	        int mid = len / 2;
    	        char[][] result = new char[len][len];
    	        for(int i = 0;i < len;i++)
    	            for(int j = 0;j < len;j++)
    	                result[i][j] = '.';
    	        for(int i = mid - 2;i <= mid + 2;i++) {
    	            result[mid][i] = '$';
    	            result[i][mid] = '$';
    	        }
    	        for(int i = 1;i <= n;i++) {
    	            for(int j = mid - 2 * i;j <= mid + 2 * i;j++) {
    	                result[mid - 2 * (i + 1)][j] = '$';
    	                result[mid + 2 * (i + 1)][j] = '$';
    	                result[j][mid - 2 * (i + 1)] = '$';
    	                result[j][mid + 2 * (i + 1)] = '$';
    	            }
    	            //左上角
    	            result[mid - 2 * i][mid - 2 * i] = '$';
    	            result[mid - 2 * i][mid - 2 * i - 1] = '$';
    	            result[mid - 2 * i - 1][mid - 2 * i] = '$';
    	            //右上角
    	            result[mid - 2 * i][mid + 2 * i] = '$';
    	            result[mid - 2 * i - 1][mid + 2 * i] = '$';
    	            result[mid - 2 * i][mid + 2 * i + 1] = '$';
    	            //左下角
    	            result[mid + 2 * i][mid - 2 * i] = '$';
    	            result[mid + 2 * i][mid - 2 * i - 1] = '$';
    	            result[mid + 2 * i + 1][mid - 2 * i] = '$';
    	            //右下角
    	            result[mid + 2 * i][mid + 2 * i] = '$';
    	            result[mid + 2 * i][mid + 2 * i + 1] = '$';
    	            result[mid + 2 * i + 1][mid + 2 * i] = '$';
    	        }
    	        for(int i = 0;i < len;i++) {
    	            for(int j = 0;j < len;j++) {
    	                System.out.print(result[i][j]);
    	            }
    	            System.out.println();
    	        }
    	    }
    	    
    	    public static void main(String[] args) {
    	        
    	        Scanner in = new Scanner(System.in);
    	        int n = in.nextInt();
    	        getResult(n);
    	    }
    
    }
    
    
    题目9、买不到的数目

    题目描述
    小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。

    小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。

    你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。

    本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。

    输入:
    两个正整数,表示每种包装中糖的颗数(都不多于1000)

    要求输出:
    一个正整数,表示最大不能买到的糖数

    不需要考虑无解的情况

    例如:
    用户输入:
    4 7
    程序应该输出:
    17

    再例如:
    用户输入:
    3 5
    程序应该输出:
    7

    资源约定:
    峰值内存消耗(含虚拟机) < 64M
    CPU消耗 < 3000ms

    请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
    注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
    注意:主类的名字必须是:Main,否则按无效代码处理。

    public class Main {
    	static int a;
    	static int b;
    	static int N=1000*100;   //定义一个尽量大的结果糖果数
    	public static void main(String[] args) {
    		Scanner input=new Scanner(System.in);
    		//获取a,b糖果颗数!
    		a=input.nextInt();
    		b=input.nextInt();
    		//定义很大的数组
    		int[] point=new int[N];
    		//标记数组的方法
    		pointArray(point);
    		//输出最大不能得数
    		if(maxGet(point)){   //一返回true,结束程序
    			return;
    		}
    	}
     
    	/**
    	 * 标注能得到结果的角标数组
    	 * @param point
    	 */
    	public static void pointArray(int[] point) {
    		//循环a的数组标记
    		for (int i = 0; i < N/a; i++) {     //x最大不会超过结果/a
    			for (int j = 0; j < (N-a*i)/b; j++) {    //代入最大结果,获得的值不会被超过
    				point[a*i+b*j]=1;   //对能获得的数进行标注
    			}
    		}
    	}
    	
    	public static boolean maxGet(int[] point) {
    		//定义计数器
    		int count=0;
    		//循环数组,如果内容为1则计数器加1,否则清0,判断计数器,如果count==a,输出  循环脚标-a 的数值,这个数就是最大数
    		for (int i = 0; i < point.length; i++) {
    			if(point[i]==1){
    				count++;
    				if(count==a){
    					System.out.println(i-a);
    					return true;
    				}
    			}else{
    				count=0;
    			}
    		}
    		return false;    //防止报错
    	}
    	
    	
    }
    
    题目10、剪格子

    题目描述
    如图p1.jpg所示,3 x 3 的格子中填写了一些整数。

    我们沿着图中的红色线剪开,得到两个部分,每个部分的数字和都是60。

    本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。
    如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。
    如果无法分割,则输出 0

    程序输入输出格式要求:
    程序先读入两个整数 m n 用空格分割 (m,n<10)
    表示表格的宽度和高度
    接下来是n行,每行m个正整数,用空格分开。每个整数不大于10000
    程序输出:在所有解中,包含左上角的分割区可能包含的最小的格子数目。

    例如:
    用户输入:
    3 3
    10 1 52
    20 30 1
    1 2 3

    则程序输出:
    3

    再例如:
    用户输入:
    4 3
    1 1 1 1
    1 30 80 2
    1 1 1 100

    则程序输出:
    10

    (参见p2.jpg)

    资源约定:
    峰值内存消耗(含虚拟机) < 64M
    CPU消耗 < 5000ms

    请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

    所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
    注意:不要使用package语句。不要使用jdk1.6及以上版本的特性。
    注意:主类的名字必须是:Main,否则按无效代码处理。

    在这里插入图片描述

    在这里插入图片描述

    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Scanner;
    import java.util.Stack;
    
    /**在蓝桥杯的测试里能AC
     * 主要思想分成3部分:
     *     1.统计矩阵里所有的数值的情况(这里用TreeMap可能会更好),搜索一个序列,这个序列加起来等于(总和/2 - 左上角的数),即这个序列加左上角的数等于总和的一半
     *     2.从左上角开始遍历,测试1得到的数据能不能拼接到一个相连的块中
     *     3.测试2得到的结果是不是正好将矩阵分成了两部分
     * 采取1的原因在于直接遍历的计算量很大,实际是可能超时的,也可以进行记忆优化,我想大概可以使用HashMap<当前的和, 计算过与否>[10][10]作为搜索的记录
     * 步骤1、2都有缺点,都能进一步优化,都能记忆处理
     **/
    public class Main {
    
    	static final int MAX  = 10005; // 输入值得最大值
    	static final int MLEN = 105;   // 输入矩阵的最大数据个数
    	
    	static int m, n;
    	static int[] nums;
    	static int[] hash;
    	static int aim;
    	
    	public static void main(String[] args) throws Exception {
    		
    		Scanner sc = new Scanner(System.in);
    		m = sc.nextInt();
    		n = sc.nextInt();
    		nums = new int[n * m];
    		hash = new int[MAX]; // 散列
    		uf = new int[m * n]; // 类似并查集,最后判断是否分割成两部分使用
    		
    		int all = 0;  // 总和
    		for(int i = 0; i < nums.length; i ++) {
    			int t = sc.nextInt();
    			nums[i] = t;
    			hash[t] ++;
    			all += t;
    		}
    		sc.close();
    		
    		hash[nums[0]] --; // 将位置0从中删除
    		
    		if(all % 2 != 0) { // 和为奇数
    			System.out.println(0);
    			return;
    		}
    		
    		aim = all / 2 - nums[0];
    		
    		findAim(0, MAX-1);
    		
    		if(!find)
    			System.out.println(0);
    		
    	}
    
    	static int[] list = new int[105]; // 当前已经选择的数值
    	static int ind = 0; // 列表当前的下标
    	
    	static boolean find = false; // 当找到了一个合理的答案之后,程序能快速结束
    	
    	public static void findAim(  // 查找一个能满足和为aim的序列
    			int sum,             // 当前的和
    			int index) {         // 遍历的位置
    		
    		if(find)
    			return;
    		if(sum > aim)
    			return;
    		if(sum == aim) {
    			if(isValid()) {
    				System.out.println(ind + 1);
    				find = true;
    			}
    			return;
    		}
    		
    		while(index > 0) {
    			if(hash[index] > 0) {
    				for(int i = hash[index]; i > -1; i --) {    // 加上n个index的值 贪心为上
    					for(int j = 0; j < i; j ++) {           // 更新链表
    						list[ind ++] = index;
    					}
    					findAim(sum + i*index, index-1);
    					ind -= i;  // 回溯
    				}
    				return;
    			}else {
    				index --;
    			}
    		}
    	}
    	
    	// 判断当前的list是否合法
    	// 需要判断
    	//   1.是否能将list数据和arr[0]拼接成一个整体
    	//   2.判断生成的结果是否可以分成两部分
    	static boolean isValid() {
    		validIndex = new HashSet<Integer>();
    		validIndex.add(1);
    		validIndex.add(m);
    		exist[0] = true; // 已经拥有0,0点
    		
    		// 将list散列化 由于每一次都建立一个MAX大小的向量是不现实的,使用Map结构记录
    		tempMap.clear();
    		for(int i = 0; i < ind; i ++) {
    			Integer old = tempMap.put(list[i], 1);     // 当前能够加入的值
    			if(old != null)
    				tempMap.put(list[i], 1 + old);
    		}
    		return dp(0);
    	}
    
    	static HashSet<Integer> validIndex;
    	static HashMap<Integer, Integer> tempMap = new HashMap<Integer,Integer>(); // 记录list中需要的数据,以及其需要的次数
    	static boolean[] exist = new boolean[MLEN]; // 当前已经拥有的位置
    	
    	static boolean dp(int count) {
    		if(count == ind) {
    			if(isTwoPart())
    				return true;
    			else
    				return false;
    		}
    		
    		// 找到当前能加入的点
    		// 结束时,addStack中包含了当前能加入的点的位置
    		// addStack里面放的是能加入的位置
    		Stack<Integer> addStack = new Stack<Integer>();
    		for(int li:validIndex) {      // 遍历当前的合法位置
    			Integer key = tempMap.get(nums[li]);
    			if(key != null && key != 0) {
    				addStack.add(li);
    			}
    		}
    		
    		// 选定一个点,加入
    		while(!addStack.isEmpty()) {
    			
    			int node = addStack.pop();
    			int type = addNode(node);
    			if(dp(count + 1))
    				return true;
    			removeNode(node, type);
    			
    		}
    		
    		return false;
    	}
    
    	public static int addNode(int node) { // 加入一个位置,返回变动的类型
    		int type = 0;
    		if((node+1)%m != 0   && !exist[node + 1])        // 如果没到行末
    			if(validIndex.add(node + 1)) type |= 1;
    		if((node+m)   <  m*n && !exist[node + m])        // 如果没到列末
    			if(validIndex.add(node + m)) type |= 2;
    		if((node-m)   >  0   && !exist[node - m])        // 不是第一行
    			if(validIndex.add(node - m)) type |= 4;
    		if(node % m   != 0   && !exist[node - 1])        // 不是第一列
    			if(validIndex.add(node - 1)) type |= 8;
    		validIndex.remove(node); // 删除自身
    		exist[node] = true;
    		tempMap.put(nums[node], tempMap.get(nums[node]) - 1);   // 维护map
    		return type;
    	}
    	
    	public static void removeNode(int node, int type) {
    		if((type & 1) != 0) validIndex.remove(node + 1);
    		if((type & 2) != 0) validIndex.remove(node + m);
    		if((type & 4) != 0) validIndex.remove(node - m);
    		if((type & 8) != 0) validIndex.remove(node - 1);
    		validIndex.add(node);
    		exist[node] = false;
    		tempMap.put(nums[node], tempMap.get(nums[node]) + 1);   // 维护map
    	}
    	
    	static int[] uf; // 类似并查集Union Find
    	
    	// 判断是否真的将表格分成两个部分
    	public static boolean isTwoPart() {
    		for(int i = 0; i < uf.length; i ++){
    			uf[i] = -1;
    		}
    		// 由于nums[0]一定是被选上的,故,先统计从nums[0]开始能合并多少个节点
    		uf[0] = 1;
    		merge(0);
    		// 找到一个没有被选择的位置,合并
    		int index = 0;
    		for(int i = 1; i < exist.length; i ++) {
    			if(!exist[i]) {
    				index = i;
    				break;
    			}
    		}
    		uf[index] = 2;
    		merge(index);
    		
    		// 经过上述两个合并,使uf中最起码存在两个集合,这两个集合是联通的,且一个被选取的,一个不被选取的
    		// 如果uf还存在-1,就说明存在不能被两个集合划分
    		for(int i = 0; i < uf.length; i ++) {
    			if(uf[i] == -1)
    				return false;
    		}
    		return true;
    	}
    	
    	// 从index位置开始,将与其相连且标签(标签指是否是否被选取)与其一致的元素合并,考虑exist数组
    	public static void merge(int index) { 
    		if((index+1)%m != 0    && exist[index] == exist[index+1]) check(index, index + 1);// 如果没到行末,并且行末和这个位置的标签一致
    		if((index+m)   <  m*n  && exist[index] == exist[index+m]) check(index, index + m);
    		if((index-m)   >  0    && exist[index] == exist[index-m]) check(index, index - m);
    		if(index % m   != 0    && exist[index] == exist[index-1]) check(index, index - 1);
    	}
    	
    	// 为了能简单merge的代码,抽取代码
    	// 实际作用是:在已经判明index和next都存在,并且两者标签相同时,应该进行怎样的操作
    	public static void check(int index, int next) {
    		if(uf[next] == uf[index])  // 说明是重复搜索
    			return;
    		else {
    			uf[next] = uf[index];
    			merge(next);           // 对next进行搜索
    		}
    	}
    
    }
    
    
  • 相关阅读:
    MySQL——MySQL用户与数据库的关系
    Kafka——Kakfa的设计思想
    Hibernate建表——将MySQL的JSON列映射到Java字段(Jpa/Hibernate——Java)
    Spring Boot——Apache Commons包作用说明
    word转html工具
    html导出word
    html导出pdf
    获取dubbo-admin.war(自己maven编译)
    ajax提交表单中文呈现乱码解决
    JVM优化
  • 原文地址:https://www.cnblogs.com/a1439775520/p/13078099.html
Copyright © 2011-2022 走看看