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进行搜索
    		}
    	}
    
    }
    
    
  • 相关阅读:
    HDU 1800 Flying to the Mars 字典树,STL中的map ,哈希树
    字典树 HDU 1075 What Are You Talking About
    字典树 HDU 1251 统计难题
    最小生成树prim算法 POJ2031
    POJ 1287 Networking 最小生成树
    次小生成树 POJ 2728
    最短路N题Tram SPFA
    poj2236 并查集
    POJ 1611并查集
    Number Sequence
  • 原文地址:https://www.cnblogs.com/a1439775520/p/12948162.html
Copyright © 2011-2022 走看看