算法笔记_125:算法集训之编程大题集一(Java)
Posted 舞动的心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法笔记_125:算法集训之编程大题集一(Java)相关的知识,希望对你有一定的参考价值。
目录
1 砝码称重
5个砝码 用天平称重时,我们希望用尽可能少的砝码组合称出尽可能多的重量。 如果只有5个砝码,重量分别是1,3,9,27,81。则它们可以组合称出1到121之间任意整数重量(砝码允许放在左右两个盘中)。 本题目要求编程实现:对用户给定的重量,给出砝码组合方案。 例如: 用户输入: 5 程序输出: 9-3-1 用户输入: 19 程序输出: 27-9+1 要求程序输出的组合总是大数在前小数在后。 可以假设用户的输入的数字符合范围1~121。
//方法1:三进制处理 package com.liu.ex1; import java.util.ArrayList; import java.util.Scanner; public class Main { public static int[] value = new int[6]; public void printResult(int n) { int count = 0; while(n > 0) { //把n转换为三进制数 value[count++] = n % 3; n = n / 3; } //对n的三进制数进行处理,得到砝码组合结果 for(int i = 0;i < 5;i++) { if(value[i] >= 2) { value[i] = value[i] - 3; value[i + 1] = value[i + 1] + 1; } } ArrayList<Integer> list = new ArrayList<Integer>(); for(int i = 5;i >= 0;i--) { if(value[i] == 1) { int a = (int) Math.pow(3, i); list.add(a); } else if(value[i] == -1) { int a = (int) (-1 * Math.pow(3, i)); list.add(a); } } System.out.print(list.get(0)); for(int i = 1;i < list.size();i++) { if(list.get(i) > 0) { System.out.print("+"); } System.out.print(list.get(i)); } return; } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); int n = in.nextInt(); test.printResult(n); } }
//方法2:DFS搜索 package com.liu.ex1; import java.util.ArrayList; import java.util.Scanner; public class Main1 { public static int[] value = {1,3,9,27,81}; public static int n = 0; public static ArrayList<Integer> result = new ArrayList<Integer>(); public int getSum() { int sum = 0; for(int i = 0;i < result.size();i++) sum += result.get(i); return sum; } public void dfs(int step) { if(step == 5) { if(getSum() == n) { int i = result.size() - 1; for(;i >= 0;i--) { if(result.get(i) != 0) { System.out.print(result.get(i)); i--; break; } } for(;i >= 0;i--) { if(result.get(i) == 0) continue; if(result.get(i) > 0) System.out.print("+"); System.out.print(result.get(i)); } } } else { for(int i = -1;i <= 1;i++) { int a = i * value[step]; result.add(a); dfs(step + 1); result.remove(result.size() - 1); } } } public static void main(String[] args) { Main1 test = new Main1(); Scanner in = new Scanner(System.in); n = in.nextInt(); test.dfs(0); } }
2 公式解析
在某些应用中,为了支持灵活性,往往用到自定义的公式。 比如,有如下的原始公式集合: int add(int x, int y): 返回x与y的和 int add(int x, int y, int z): 返回x,y,z三个数的和 int min(int x, int y): 返回x,y中较小的值 int max(int x, int y): 返回x,y中较大的值 int doubleMe(int x): 返回 x 的2倍 给出一个自定义公式串 add(min(5,3),max(2,8),add(1,doubleMe(1))) 通过手工计算可以得出结果为:14 本题的任务是:编写一个解析程序,能够对由上述原始公式任意组合出来的公式计算其结果。也就是输入一个自定义公式串,输出它的计算结果(可以不考虑输入公式本身有语法错误的情况)。 输入的公式串中可以含有多余的空格,类似: add( min(5, 3) , max(2 , 8) ) 也是合法的公式。 程序输入:公式串 程序输出:该公式的计算值
package com.liu.ex2; import java.util.Scanner; import java.util.Stack; public class Main { public static Stack<String> operation = new Stack<String>(); //存放运算符 public static Stack<Character> bracket = new Stack<Character>(); //存放左括号 public static Stack<Integer> number = new Stack<Integer>(); //存放运算参数 public static Stack<Integer> count = new Stack<Integer>(); //存放运算符参数个数 public int add(int[] N) { if(N.length == 3) return N[0] + N[1] + N[2]; return N[0] + N[1]; } public int max(int[] N) { return N[0] > N[1] ? N[0] : N[1]; } public int min(int[] N) { return N[0] < N[1] ? N[0] : N[1]; } public int doubleMe(int[] N) { return 2 * N[0]; } public boolean judgeChar(char s) { if(s >= ‘a‘ && s <= ‘z‘ || s >= ‘A‘ && s <= ‘Z‘) return true; return false; } public boolean judgeNumber(char s) { if(s >= ‘0‘ && s <= ‘9‘) return true; return false; } public void getResult(String A) { String temp = ""; for(int i = 0;i < A.length();i++) { if(judgeChar(A.charAt(i))) { temp = temp + A.charAt(i); i = i + 1; while(judgeChar(A.charAt(i))) { temp = temp + A.charAt(i); i++; } i = i - 1; operation.push(temp); count.push(0); //刚寻找到一个运算符,并初始化一个参数个数为0 temp = ""; } else if(A.charAt(i) == ‘(‘) { bracket.push(A.charAt(i)); } else if(judgeNumber(A.charAt(i))) { temp = temp + A.charAt(i); i = i + 1; while(judgeNumber(A.charAt(i))) { temp = temp + A.charAt(i); i++; } i = i - 1; number.push(Integer.valueOf(temp)); count.push(count.pop() + 1); //此处用于计算当前栈顶运算符实际参数个数 temp = ""; } else if(A.charAt(i) == ‘)‘) { //此时要进行运算 bracket.pop(); //栈顶左括号出栈 String tempOpera = operation.pop(); int[] N = new int[count.pop()]; if(!count.empty()) count.push(count.pop() + 1); for(int j = 0;j < N.length;j++) N[j] = number.pop(); int result = 0; if(tempOpera.equals("add")) result = add(N); else if(tempOpera.equals("max")) result = max(N); else if(tempOpera.equals("min")) result = min(N); else if(tempOpera.equals("doubleMe")) result = doubleMe(N); number.push(result); } } } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); String A = in.nextLine(); test.getResult(A); System.out.println(number.pop()); } }
3 购物券消费方案
公司发了某商店的购物券1000元,限定只能购买店中的m种商品。每种商品的价格分别为m1,m2,…,要求程序列出所有的正好能消费完该购物券的不同购物方法。 程序输入: 第一行是一个整数m,代表可购买的商品的种类数。 接下来是m个整数,每个1行,分别代表这m种商品的单价(0<m<1000)。 程序输出: 第一行是一个整数,表示共有多少种方案 第二行开始,每种方案占1行,表示对每种商品购买的数量,中间用空格分隔。 例如: 输入: 2 200 300 则应输出: 2 2 2 5 0 输入: 2 500 800 则应输出: 1 2 0 输入: 1 999 则应输出: 0 多个方案间的顺序不重要。
package com.liu.ex3; import java.util.ArrayList; import java.util.Scanner; public class Main { public static ArrayList<Integer> list = new ArrayList<Integer>(); public static int[][] value; public static int m; public static int count = 0; public static String result = ""; public void dfs(int sum, int step) { if(step == m) { if(sum == 1000) { for(int i = 0;i < list.size();i++) { result += list.get(i)+" "; } result += "\n"; count++; } return; } else { for(int i = 0;i <= value[step][1];i++) { sum += value[step][0] * i; list.add(i); dfs(sum, step + 1); sum -= value[step][0] * i; list.remove(list.size() - 1); } } } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); m = in.nextInt(); value = new int[m][2]; for(int i = 0;i < m;i++) { int a = in.nextInt(); int num = 1000 / a; value[i][0] = a; value[i][1] = num; } test.dfs(0, 0); if(count == 0) System.out.println("0"); else System.out.println(count+"\n"+result); } }
4 机器人行走
某少年宫引进了一批机器人小车。可以接受预先输入的指令,按指令行动。小车的基本动作很简单,只有3种:左转(记为L),右转(记为R),向前走若干厘米(直接记数字)。 例如,我们可以对小车输入如下的指令: 15L10R5LRR10R20 则,小车先直行15厘米,左转,再走10厘米,再右转,... 不难看出,对于此指令串,小车又回到了出发地。 你的任务是:编写程序,由用户输入指令,程序输出每条指令执行后小车位置与指令执行前小车位置的直线距离。 【输入、输出格式要求】 用户先输入一个整数n(n<100),表示接下来将有n条指令。 接下来输入n条指令。每条指令只由L、R和数字组成(数字是0~100之间的整数) 每条指令的长度不超过256个字符。 程序则输出n行结果。 每条结果表示小车执行相应的指令前后位置的直线距离。要求四舍五入到小数后2位。 例如:用户输入: 5 L100R50R10 3LLL5RR4L12 LL 100R 5L5L5L5 则程序输出: 102.96 9.06 0.00 100.00 0.00 【注意】 请仔细调试!您的程序只有能运行出正确结果的时候才有机会得分!
package com.liu.ex4; import java.util.Scanner; public class Main { public static int[] position = {0,1,2,3}; //表示机器人朝向,分别为上、左、下、右 public String getResult(String A) { //机器人起始朝向默认为向上 int area = position[0]; double x = 0, y = 0; for(int i = 0;i < A.length();i++) { String temp = ""; if(A.charAt(i) == ‘L‘) { area = (area + 1) % 4; } else if(A.charAt(i) == ‘R‘) { if(area == 0) area = 3; else area = area - 1; } else { for(;i < A.length();i++) { if(A.charAt(i) == ‘L‘ || A.charAt(i) == ‘R‘) { i = i - 1; break; } temp += A.charAt(i); } int num = Integer.valueOf(temp); if(area == 0) y = y + num; else if(area == 1) x = x - num; else if(area == 2) y = y - num; else if(area == 3) x = x + num; } } double result = x * x + y * y; result = Math.sqrt(result); String tempResult = String.format("%.2f", result); return tempResult; } public void printResult(String[] A) { String[] result = new String[A.length]; for(int i = 0;i < A.length;i++) { result[i] = getResult(A[i]); } for(int i = 0;i < A.length;i++) System.out.println(result[i]); return; } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); int n = in.nextInt(); in.nextLine(); String[] A = new String[n]; for(int i = 0;i < n;i++) A[i] = in.nextLine(); test.printResult(A); } }
5 角谷步数
你听说过角谷猜想吗? 任意的正整数,比如 5, 我们从它开始,如下规则计算: 如果是偶数,则除以2,如果是奇数,则乘以3再加1. 如此循环,最终必会得到“1” ! 比如 5 的处理过程是: 5 16 8 4 2 1 一个正整数经过多少步才能变成1, 称为角谷步数。 对于5而言,步数也是5 对于1,步数为0 本题的要求是,从标准输入给定一个整数n(1<n<300)表示角谷步数 求满足这个角谷步数的最小的正整数 例如: 输入: 3 则输出: 8 输入: 4 则输出: 16 输入: 7 则输出: 3
package com.liu.ex5; import java.util.ArrayList; import java.util.Scanner; public class Main { public static ArrayList<Integer> list = new ArrayList<Integer>(); public void getResult() { list.add(0); for(int i = 1;i <= 300000;i++) { int tempi = i; int count = 0; while(tempi > 1) { if(tempi % 2 == 0) { tempi = tempi / 2; count++; } else { tempi = tempi * 3 + 1; count++; } } list.add(count); } return; } public void printResult(int n) { getResult(); int result = list.indexOf(n); System.out.println(result); return; } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); int n = in.nextInt(); test.printResult(n); } }
6 矩形区域的交和并
在编写图形界面软件的时候,经常会遇到处理两个矩形的关系。 如图【1.jpg】所示,矩形的交集指的是:两个矩形重叠区的矩形,当然也可能不存在(参看【2.jpg】)。两个矩形的并集指的是:能包含这两个矩形的最小矩形,它一定是存在的。 本题目的要求就是:由用户输入两个矩形的坐标,程序输出它们的交集和并集矩形。 矩形坐标的输入格式是输入两个对角点坐标,注意,不保证是哪个对角,也不保证顺序(你可以体会一下,在桌面上拖动鼠标拉矩形,4个方向都可以的)。 输入数据格式: x1,y1,x2,y2 x1,y1,x2,y2 数据共两行,每行表示一个矩形。每行是两个点的坐标。x坐标在左,y坐标在右。坐标系统是:屏幕左上角为(0,0),x坐标水平向右增大;y坐标垂直向下增大。 要求程序输出格式: x1,y1,长度,高度 x1,y1,长度,高度 也是两行数据,分别表示交集和并集。如果交集不存在,则输出“不存在” 前边两项是左上角的坐标。后边是矩形的长度和高度。 例如,用户输入: 100,220,300,100 150,150,300,300 则程序输出: 150,150,150,70 100,100,200,200 例如,用户输入: 10,10,20,20 30,30,40,40 则程序输出: 不存在 10,10,30,30 注意: 请仔细调试!您的程序只有能运行出正确结果的时候才有机会得分! 在评卷时使用的输入数据与试卷中给出的实例数据可能是不同的。
package com.liu.ex6; import java.util.Scanner; public class Main { public static int[][] point = new int[4][2]; public static int[][] result = new int[2][4]; public void getResult(String[] A) { String[] tempA1 = A[0].split(","); String[] tempA2 = A[1].split(","); point[0][0] = Integer.valueOf(tempA1[0]); point[0][1] = Integer.valueOf(tempA1[1]); point[1][0] = Integer.valueOf(tempA1[2]); point[1][1] = Integer.valueOf(tempA1[3]); point[2][0] = Integer.valueOf(tempA2[0]); point[2][1] = Integer.valueOf(tempA2[1]); point[3][0] = Integer.valueOf(tempA2[2]); point[3][1] = Integer.valueOf(tempA2[3]); //第一个矩形对角线坐标 int x1 = Math.min(point[0][0], point[1][0]); int y1 = Math.min(point[0][1], point[1][1]); int x2 = Math.max(point[0][0], point[1][0]); int y2 = Math.max(point[0][1], point[1][1]); //第二个矩形对角线坐标 int x3 = Math.min(point[2][0], point[3][0]); int y3 = Math.min(point[2][1], point[3][1]); int x4 = Math.max(point[2][0], point[3][0]); int y4 = Math.max(point[2][1], point[3][1]); if(x3 >= x2 || x1 >= x4 || y3 >= y2 || y1 >= y4) { result[0][0] = -1; //表示交集不存在 } else { int x = 0, y = 0, len = 0, high = 0; if(x3 >=x1 && x3 < x2) { if(x4 >= x2) { x = x3; len = x2 - x3; } else { x = x3; len = x4 - x3; } } else if(x1 >= x3 && x1 < x4) { if(x2 >= x4) { x = x1; len = x4 - x1; } else { x = x1; len = x2 - x1; } } if(y3 >= y1 && y3 < y2) { if(y4 >= y2) { y = y3; high = y2 - y3; } else { y = y3; high = y4 - y3; } } else if(y1 >= y3 && y1 < y4) { if(y2 >= y4) { y = y1; high = y4 - y1; } else { y = y1; high = y2 - y1; } } result[0][0] = x; result[0][1] = y; result[0][2] = len; result[0][3] = high; } int x = Math.min(x1, x3); int y = Math.min(y1, y3); int len = Math.max(x2, x4) - x; int high = Math.max(y2, y4) - y; result[1][0] = x; result[1][1] = y; result[1][2] = len; result[1][3] = high; } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); String[] A = new String[2]; for(int i = 0;i < 2;i++) { A[i] = in.next(); } test.getResult(A); for(int i = 0;i < 2;i++) { for(int j = 0;j < 3;j++) { if(i == 0 && result[0][0] == -1) { System.out.println("不存在"); break; } else { System.out.print(result[i][j]+","); } } if(result[0][0] != -1) System.out.println(result[i][3]); else { if(i == 1) System.out.println(result[i][3]); } } } }
7 矩阵变换加密法
一种Playfair密码变种加密方法如下:首先选择一个密钥单词(称为pair)(字母不重复,且都为小写字母),然后与字母表中其他字母一起填入至一个5x5的方阵中,填入方法如下: 1.首先按行填入密钥串。 2.紧接其后,按字母序按行填入不在密钥串中的字母。 3.由于方阵中只有25个位置,最后剩下的那个字母则不需变换。 如果密钥为youandme,则该方阵如下: y o u a n d m e b c f g h i j k l p q r s t v w x 在加密一对字母时,如am,在方阵中找到以这两个字母为顶点的矩形: o u a m e b 这对字母的加密字母为该矩形的另一对顶点,如本例中为ob。 请设计程序,使用上述方法对输入串进行加密,并输出加密后的串。 另外有如下规定: 1、一对一对取字母,如果最后只剩下一个字母,则不变换,直接放入加密串中; 2、如果一对字母中的两个字母相同,则不变换,直接放入加密串中; 3、如果一对字母中有一个字母不在正方形中,则不变换,直接放入加密串中; 4、如果字母对出现在方阵中的同一行或同一列,如df或hi,则只需简单对调这两个字母,即变换为fd或ih; 5、如果在正方形中能够找到以字母对为顶点的矩形,假如字母对为am,则该矩形的另一对顶点字母中,与a同行的字母应在前面,在上例中应是ob;同样若待变换的字母对为ta,则变换后的字母对应为wo; 6、本程序中输入串均为小写字母,并不含标点、空格或其它字符。 解密方法与加密相同,即对加密后的字符串再加密,将得到原始串。 要求输入形式如下: 从控制台输入两行字符串,第一行为密钥单词(长度小于等于25),第二行为待加密字符串(长度小于等于50),两行字符串末尾都有一个回车换行符,并且两行字符串均为小写字母,不含其它字符。 在标准输出上输出加密后的字符串。 例如,若输入: youandme welcometohangzhou 则表示输入的密钥单词为youandme,形成的正方形如上所示;待加密字符串为welcometohangzhou。在正方形中可以找到以第一对字母we为顶点的矩形,对应另一对顶点字母为vb,因此加密后为vb,同理可找到与字母对lc,et,oh,ho对应的顶点字母对。而字母对om位于上述正方形中的同一列,所以直接以颠倒这两个字母来加密,即为mo,字母对an同理。字母对gz中的z不在上述正方形中,因此原样放到加密串中。最后剩一个字母u也原样输出。 因此输出的结果为: vbrmmomvugnagzguu
package com.liu.ex7; import java.util.ArrayList; import java.util.Scanner; public class Main { public static char[][] value = new char[5][5]; public static boolean[] used = new boolean[26]; public int[] getPosition(char s) { int[] position = new int[2]; for(int i = 0;i < 5;i++) { for(int j = 0;j < 5;j++) { if(value[i][j] == s) { position[0] = i; position[1] = j; return position; } } } return position; } public void getResult(String pair, String psw) { for(int i = 0;i < 26;i++) used[i] = false; ArrayList<Character> list = new ArrayList<Character>(); for(int i = 0;i < pair.length();i++) { list.add(pair.charAt(i)); int temp = pair.charAt(i) - ‘a‘; used[temp] = true; } while(list.size() < 25) { for(int i = 0;i < 26;i++) { if(used[i] == false) { char temp = (char) (‘a‘ + i); list.add(temp); used[i] = true; } if(list.size() == 25) break; } } char unUsed = ‘a‘; for(int i = 0;i < 26;i++) { if(used[i] == false) { unUsed = (char) (unUsed + i); break; } } int count = 0; for(int i = 0;i < 5;i++) for(int j = 0;j < 5;j++) value[i][j] = list.get(count++); int len = psw.length(); ArrayList<Character> result = new ArrayList<Character>(); if(len % 2 == 1) len = len - 1; for(int i = 0;i < len;i = i + 2) { char temp1 = psw.charAt(i); char temp2 = psw.charAt(i + 1); int[] position1 = getPosition(temp1); int[] position2 = getPosition(temp2); if(temp1 == temp2) { result.add(temp1); result.add(temp2); } else if(temp1 == unUsed || temp2 == unUsed) { result.add(temp1); result.add(temp2); } else if(position1[0] == position2[0] || position1[1] == position2[1]) { result.add(temp2); result.add(temp1); } else { temp1 = value[position1[0]][position2[1]]; temp2 = value[position2[0]][position1[1]]; result.add(temp1); result.add(temp2); } } if(psw.length() % 2 == 1) result.add(psw.charAt(psw.length() - 1)); for(int i = 0;i < result.size();i++) System.out.print(result.get(i)); return; } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); String pair = in.next(); String psw = in.next(); test.getResult(pair, psw); } }
8 控制台表格
画表格 在图形环境中很容易做出漂亮的表格。但在控制台环境中就比较困难了。有的时候可以用一些符号大略地模拟:(word文档中可能不整齐,拷贝到记事本中看) +-------+------+ |abc |xyz=tt| +-------+------+ |hellomm|t2 | +-------+------+ 本题目要求设计一个程序,把用户输入的内容用这种“准表格”的方式展现出来。具体的要求是: 用户输入的第一行是一个整数,表示接下来有多少行信息。接下来的每行由若干单元组成。单元间用逗号分开。 程序输出:用表格方式重新展现的输入内容。 例如: 用户输入: 3 cat,dog,good-luck 1,2,5 do not use,,that 则程序输出:(word文档中可能不整齐,拷贝到记事本中看) +----------+---+---------+ |cat |dog|good-luck| +----------+---+---------+ |1 |2 |5 | +----------+---+---------+ |do not use| |that | +----------+---+---------+ 从中不难看出: 两个连续的逗号表示中间有一个内容为空的单元 列的数目由最大的单元数的那行决定 列的宽度由同列的最宽的单元决定 单元格中的信息左对齐 可以假设:用户输入的最大行数为30,可能的最多列数为40。
package com.liu.ex8; import java.util.Scanner; public class Main { public void printResult(String[] A) { String[] tempA = A[0].split(","); int maxLen = tempA.length; for(int i = 1;i < A.length;i++) { tempA = A[i].split(","); if(maxLen < tempA.length) maxLen = tempA.length; } String[][] valueA = new String[A.length][maxLen]; for(int i = 0;i < valueA.length;i++) for(int j = 0;j < valueA[0].length;j++) valueA[i][j] = ""; for(int i = 0;i < A.length;i++) { tempA = A[i].split(","); for(int j = 0;j < tempA.length;j++) valueA[i][j] = tempA[j]; } int[] maxJ = new int[maxLen]; for(int j = 0;j < maxLen;j++) { for(int i = 0;i < A.length;i++) { if(valueA[i][j].length() > maxJ[j]) maxJ[j] = valueA[i][j].length(); } } StringBuilder opera = new StringBuilder("+"); for(int j = 0;j < maxJ.length;j++) { for(int k = 0;k < maxJ[j];k++) opera.append(‘-‘); opera.append(‘+‘); } for(int i = 0;i < valueA.length;i++) { System.out.println(opera); System.out.print("|"); for(int j = 0;j < valueA[0].length;j++) { int len = maxJ[j] - valueA[i][j].length(); String format = ""; if(len == 0) format = "" + "%s"; else format = "%" + len + "s"; System.out.print(valueA[i][j]); System.out.printf(format, ""); System.out.print("|"); } System.out.println(); } System.out.println(opera); return; } public static void main(String[] args) { Main test = new Main(); Scanner in = new Scanner(System.in); int n = in.nextInt(); in.nextLine(); String[] A = new String[n]; for(int i = 0;i < n;i++) A[i] = in.nextLine(); test.printResult(A); } }
9 拉丁方块填数字
“数独”是当下炙手可热的智力游戏。一般认为它的起源是“拉丁方块”,是大数学家欧拉于1783年发明的。 如图[1.jpg]所示:6x6的小格被分为6个部分(图中用不同的颜色区分),每个部分含有6个小格(以下也称为分组)。 开始的时候,某些小格中已经填写了字母(ABCDEF之一)。需要在所有剩下的小格中补填字母。 全部填好后,必须满足如下约束: 1. 所填字母只允许是A,B,C,D,E,F 中的某一个。 2. 每行的6个小格中,所填写的字母不能重复。 3. 每列的6个小格中,所填写的字母不能重复。 4. 每个分组(参见图中不同颜色表示)包含的6个小格中,所填写的字母不能重复。 为了表示上的方便,我们用下面的6阶方阵来表示图[1.jpg]对应的分组情况(组号为0~5): 000011 022013 221113 243333 244455 445555 用下面的数据表示其已有字母的填写情况: 02C 03B 05A 20D 35E 53F 很明显,第一列表示行号,第二列表示列号,第三列表示填写的字母。行号、列号都从0开始计算。 一种可行的填写方案(此题刚好答案唯一)为: E F C B D A A C E D F B D A B E C F F B D C A E B D F A E C C E A F B D 你的任务是:编写程序,对一般的拉丁方块问题求解,如果多解,要求找到所有解。 【输入、输出格式要求】 用户首先输入6行数据,表示拉丁方块的分组情况。 接着用户输入一个整数n (n<36), 表示接下来的数据行数 接着输入n行数据,每行表示一个预先填写的字母。 程序则输出所有可能的解(各个解间的顺序不重要)。 每个解占用7行。 即,先输出一个整数,表示该解的序号(从1开始),接着输出一个6x6的字母方阵,表示该解。 解的字母之间用空格分开。 如果找不到任何满足条件的解,则输出“无解” 例如:用户输入: 000011 022013 221113 243333 244455 445555 6 02C 03B 05A 20D 35E 53F 则程序输出: 1 E F C B D A A C E D F B D A B E C F F B D C A E B D F A E C C E A F B D 再如,用户输入: 001111 002113 022243 022443 544433 555553 7 04B 05A 13D 14C 24E 50C 51A 则程序输出: 1 D C E F B A E F A D C B A B F C E D B E D A F C F D C B A E C A B E D F 2 D C E F B A E F A D C B A D F B E C B E C A F D F B D C A E C A B E D F 3 D C F E B A A E B D C F F D A C E B B F E A D C E B C F A D C A D B F E 4 D C F E B A B E A D C F A D C F E B F B E A D C E F B C A D C A D B F E 5 D C F E B A E F A D C B A B C F E D B E D A F C F D B C A E C A E B D F 6 D C F E B A E F A D C B A B D F E C B E C A F D F D B C A E C A E B D F 7 D C F E B A E F A D C B A D B F E C B E C A F D F B D C A E C A E B D F 8 D C F E B A F E A D C B A D B C E F B F E A D C E B C F A D C A D B F E 9 D C F E B A F E A D C B A F C B E D B D E A F C E B D C A F C A B F D E
package com.liu.ex9; import java.util.HashSet; import java.util.Scanner; public class Main { public static int[][] group = new int[6][6]; //输入分组情况 public static char[][] result = new char[6][6]; //满足题意的填充结果 public static int[][] row = new int[6][6]; //检测6行是否均为不同字母 public static int[][] col = new int[6][6]; //检测6列是否均为不同字母 public static int[][] set = new int[6][6]; //检测0~5组是否均为不同字母 public static char[] value = {‘A‘,‘B‘,‘C‘,‘D‘,‘E‘,‘F‘}; //填充字母 public static int count = 0; //统计最终解个数 public HashSet<String> hash = new HashSet<String>(); public boolean check() { StringBuilder temp = new StringBuilder(""); for(int i = 0;i < 6;i++) { for(int j = 0;j < 6;j++) temp.append(result[i][j]+" "); temp.append("\n"); } String A = temp.toString(); if(hash.contains(A)) return false; return true; } public void dfs(int step) { if(step >= 37) { if(check()) { //检测是否有重复解 count++; System.out.println(count); StringBuilder temp = new StringBuilder(""); for(int i = 0;i < 6;i++) { for(int j = 0;j < 6;j++) temp.append(result[i][j]+" "); temp.append("\n"); } hash.add(temp.toString()); System.out.print(temp); } return; } else { int tempRow = (step - 1) / 6; int tempCol = (step - 1) % 6; int tempSet = group[tempRow][tempCol]; for(int i = 0;i < 6;i++) { if(result[tempRow][tempCol] == ‘-‘) { char temp = (char) (‘A‘ + i); if(row[tempRow][i] == 0 && col[tempCol][i] == 0 && set[tempSet][i] == 0) { result[tempRow][tempCol] = temp; row[tempRow][i] = 1; col[tempCol][i] = 1; set[tempSet][i] = 1; dfs(step + 1); result[tempRow][tempCol] = ‘-‘; //回溯处理 row[tempRow][i] = 0; col[tempCol][i] = 0; set[tempSet][i] = 0; } else { continue; } } else { dfs(step + 1); } } } return; } public static void main(String[] args) { Main test = new Main(); for(int i = 0;i < 6;i++) for(int j = 0;j < 6;j++) result[i][j] = ‘-‘; //初始化为填充格子字符为‘-‘ Scanner in = new Scanner(System.in); for(int i = 0;i < 6;i++) { String temp = in.nextLine(); for(int j = 0;j < 6;j++) group[i][j] = temp.charAt(j) - ‘0‘; } int n = in.nextInt(); in.nextLine(); for(int i = 0;i < n;i++) { String temp = in.nextLine(); int a = temp.charAt(0) - ‘0‘; int b = temp.charAt(1) - ‘0‘; int v = temp.charAt(2) - ‘A‘; result[a][b] = temp.charAt(2); row[a][v] = 1; //表示第a行位于第v个的位置,已经填充 col[b][v] = 1; //表示第b列位于第v个的位置,已经填充 int tempSet = group[a][b]; //获取(a,b)小组组号 set[tempSet][v] = 1; //表示第tempSet小组第v个位置,已经填充 } test.dfs(1); } }
10 立方和等式
考虑方程式:a^3 + b^3 = c^3 + d^3 其中:“^”表示乘方。a、b、c、d是互不相同的小于30的正整数。 这个方程有很多解。比如: a = 1,b=12,c=9,d=10 就是一个解。因为:1的立方加12的立方等于1729,而9的立方加10的立方也等于1729。 当然,a=12,b=1,c=9,d=10 显然也是解。 如果不计abcd交换次序的情况,这算同一个解。 你的任务是:找到所有小于30的不同的正整数解。把a b c d按从小到大排列,用逗号分隔,每个解占用1行。比如,刚才的解输出为: 1,9,10,12 不同解间的顺序可以不考虑。
package com.liu.ex10; import java.util.ArrayList; import java.util.Collections; public class Main { public static boolean judge(ArrayList<Integer> tempList) { ArrayList<Integer> list = new ArrayList<Integer>(); for(int i = 0;i < tempList.size();i++) list.add(tempList.get(i)); Collections.sort(list); for(int i = 1;i < list.size();i++) { if(list.get(i - 1) == list.get(i)) return false; } return true; } public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); for(int a = 1;a < 30;a++) { for(int b = 1;b < 30;b++) { for(int c = 1;c < 30;c++) { for(int d = 1;d < 30;d++) { ArrayList<Integer> tempList = new ArrayList<Integer>(); tempList.add(a); tempList.add(b); tempList.add(c); tempList.add(d); if(judge(tempList) == true) { if(a*a*a + b*b*b == c*c*c + d*d*d) { Collections.sort(tempList); String A = ""+tempList.get(0)+","+tempList.get(1)+","+tempList.get(2)+","+tempList.get(3); if(!list.contains(A)) list.add(A); } } } } } } for(int i = 0;i < list.size();i++) System.out.println(list.get(i)); } }
以上是关于算法笔记_125:算法集训之编程大题集一(Java)的主要内容,如果未能解决你的问题,请参考以下文章