全排列问题
-
代分数
100 可以表示为带分数的形式:100 = 3 + 69258 / 714
带分数中,数字1~9分别出现且只出现一次(不包含0)。
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。package Bag; import java.util.Scanner; public class Main{ static int aws = 0; static int[] a = new int[10]; static int[] flag = new int[10]; public static void main(String[] args) { int m; Scanner sc =new Scanner(System.in); m = sc.nextInt(); DFS(1, 10, m); System.out.println(aws); } static int sum(int start, int end) { int i, sum = 0; for (i = start; i < end; i++) sum = sum * 10 + a[i + 1]; return sum; } static void Found(int a[], int n, int m)//将DFS中的每一个全排列结果放在Found函数中检验 { int i, j; for (i = 1; i < n; i++) { // 选出a数组中0-i的数 int m1 = sum(0, i);//第一个数从1至9开始选 if (m1 >= m) return;//不满足第一个数<m的直接淘汰 for (j = i + (n - i) / 2; j < n - 1; j++) { int m2 = sum(i, j);//第二个数 int m3 = sum(j, n - 1);//第三个数 if (m2 > m3 && m2 % m3 == 0 && m == m1 + m2 / m3) { aws++; } } } } // 1 10 输入的数字 static void DFS(int start, int n, int m)//对1~9进行全排列 { int i; // 当a数组里储存了1-9的全排列之后 if (start == n) Found(a, n, m); else { for (i = 1; i < n; i++) { if (flag[i]==1) continue; a[start] = i; flag[i] = 1; DFS(start + 1, n, m);//选择好一位开始选下一位 flag[i] = 0; } } } }
-
六角填数
在六角形中,填入1~12的数字,使得每条直线上的数字之和都相同。计算位置
*
位置代表的数字是多少
public class 六角填数 { private static long Begin = 0; //与本题无关,只是计算一下程序总共耗时多长时间 private static long End = 0; //与本题无关,只是计算一下程序总共耗时多长时间 public static void Swap(int[] c,int i,int j) { int tmp = c[i]; c[i] = c[j]; c[j] = tmp; } public static void AllPermutation(int[] c,int start) { if(start==c.length-1) //说明已找到一个排列 { if(c[0]==8&&c[3]==3&&c[4]==1) //判断六角形的下标(0,3,4)对应的值是否符合已给的三个值 { int A = c[0]+c[1]+c[2]+c[3]; //第一条边的和 int B = c[4]+c[5]+c[1]+c[6]; //第二条边的和 int C = c[0]+c[5]+c[7]+c[8]; //第三条边的和 int D = c[4]+c[7]+c[9]+c[11]; //第四条边的和 int E = c[8]+c[9]+c[10]+c[3]; //第五条边的和 int F = c[6]+c[2]+c[10]+c[11]; //第六条边的和 if(A==B&&B==C&&C==D&&D==E&&E==F) { for(int i=0,t=c.length;i<t;++i) //把所有下标的值都输出 { if(i!=0) { System.out.print(" "); } System.out.print(c[i]); } System.out.println("\nc[1]:"+c[1]); //所求*值 End = System.currentTimeMillis() - Begin; //与本题无关,只是计算一下程序总共耗时多长时间 System.out.println("耗时:"+End+"ms"); //总共耗时 System.exit(0); //退出java虚拟机 } } } else { for(int i=start,t=c.length;i<t;++i) { Swap(c,i,start); AllPermutation(c,start+1); Swap(c,start,i); } } } public static void main(String[] args) { int[] c = {8,2,4,3,1,5,6,7,9,10,11,12}; //根据六角形的下标位置赋值 Begin = System.currentTimeMillis(); //与本题无关,只是计算一下程序总共耗时多长时间 AllPermutation(c,0); //全排列算法 }
-
九数分三组
1~9的数字可以组成3个3位数,设为:A,B,C, 现在要求满足如下关系:
B = 2 * A
C = 3 * A
请你写出A的所有可能答案,数字间用空格分开,数字按升序排列。public class Main{ public static int[] a = new int[15]; public static boolean[] book = new boolean[15]; public static int n=9; public static void dfs(int step) { if(step== n+1) { if(2*(a[1]*100+a[2]*10+a[3]) == a[4]*100+a[5]*10+a[6] && 3*(a[1]*100+a[2]*10+a[3]) == a[7]*100+a[8]*10+a[9]) { System.out.println(a[1]+" "+a[2]+" "+a[3]); } return; } for(int x=1;x<=9;x++) { if(!book[x]) { book[x] = true; a[step] = x; dfs(step+1); book[x] = false; } } } public static void main(String[] args) { dfs(1); } }
-
寒假作业
每个方框代表1-13种的某个数字,不能重复,多少种方案
□ + □ = □
□ - □ = □
□ × □ = □
□ ÷ □ = □public class Main { static int count=0; public static void main(String[] args) { int[] arr = {1,2,3,4,5,6,7,8,9,10,11,12,13}; dfs(arr,0); System.out.println(count); } public static void dfs(int[] arr,int start) { if(start>=3) { if(arr[0]+arr[1]!=arr[2]) return ; } if(start>=6) { if(arr[3]-arr[4]!=arr[5]) return ; } if(start>=9) { if(arr[6]*arr[7]!=arr[8]) return ; } if(start>=12) { if(arr[10]*arr[11]==arr[9]) { count++; return ; } } // 递归计算1-13的全排列 for(int i=start;i<arr.length;i++) { int tmp = arr[i]; arr[i] = arr[start]; arr[start] = tmp; dfs(arr,start+1); tmp = arr[i]; arr[i] = arr[start]; arr[start] = tmp; } } }
-
剪邮票
连着的5个
public class Main { static int a[]=new int[5]; static int ants=0; static Set<HashSet<Integer>> set=new HashSet<HashSet<Integer>>(); static boolean used[]=new boolean[13]; /** * 超级集合Set用于判重 * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Arrays.fill(used, false); dfs(0); System.out.println(set.size()); } //判断x,y是否相邻 public static boolean adj(int x,int y){ if(x>y){ int t=x; x=y; y=t; } // y - x == 1可以证明左右相邻 // x % 4 != 0 是为了排除54这种拐弯情况 if(y-x==4||(y-x==1&&x%4!=0)) return true; return false; } //判断a[0]-a[x-1]中是否有元素与i相邻 public static boolean check(int x,int i){ if(x==0) return true; for(int k=0;k<x;k++){ if(adj(a[k],i)) return true; } return false; } public static void dfs(int x){ if(x==5){ HashSet<Integer> s = new HashSet<Integer>(); for(int k=0;k<5;k++){ s.add(a[k]); } //将当前数组装换成集合加入到超级集合中,最后超级集合的大小就是不重复的满足条件的结果个数 set.add(s); return ; } for(int i=1;i<=12;i++){ if(!used[i]&&check(x,i)){//数字i没被用过并且i还有a[0]-a[x-1]中某个数相邻 a[x]=i; used[i]=true; dfs(x+1); used[i]=false; } } } }
-
9数算式
9213 x 85674 = 789314562
左边的乘数和被乘数正好用到了1~9的所有数字,每个1次。
而乘积恰好也是用到了1~9的所有数字,并且每个1次。
请你借助计算机的强大计算能力,找出满足如上要求的9数算式一共有多少个?public class Main { static int[] book = new int[10];//下边有注释 static int[] result = new int[10];//下边也有注释 static int count = 0;//计数 //将dfs生成的10位数全排列序列转成字符串 static String getString(){ StringBuilder stringBuilder = new StringBuilder(); /*当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。StringBuilder 类在 Java 5 中被提出它和 StringBuffer之间的最大不同在于StringBuilder的方法不是线程安全的(不能同步访问)。由于 StringBuilder相较于 StringBuffer有速度优势所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下则必须使用 StringBuffer 类。*/ for (int i = 0; i < result.length; i++) {//result.length返回长度(字符数) stringBuilder.append(String.valueOf(result[i])); //append将指定的字符串追加到此字符序列;String.valueOf(int i):将int变量i转换成字符串 } return stringBuilder.toString();//返回此序列中数据的字符串表示形式 }//把结果变成字符串 //判断这个数是否由不重复的数组成 static boolean check(int num){ if (String.valueOf(num).length()!=9 || String.valueOf(num).indexOf("0")!=-1) { //int indexOf(String str): 返回指定字符在字符串中第一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。 return false; } //哈希表判重,如果重复会插不进去 Set<Integer> set = new HashSet<Integer>();//创建set储存整数 char[] charArray = String.valueOf(num).toCharArray();//toCharArray() 方法将字符串转换为字符数组。 //等式右边的含义:先把num这个整数转换成字符串,再把num转换成的字符串转换成字符数组 //charArray字符数组储存num转换成的字符数组 //所以下一行的charArray.length的含义就是num转换成的字符数组charArray的长度 for (int i = 0; i < charArray.length; i++) { if (set.add((int) charArray[i])==false) {//add()向集合中添加元素,不能添加重复的(重复的添加不了) return false; } } return true; } //dfs枚举0-9的全排列,看是否符合条件 static void dfs(int deep){//deep当前是第几位 for (int i = 0; i <=9; i++) { if (book[i]==0) {//标记i有没有用过 book[i] = 1; result[deep] = i;//储存deep位是什么 dfs(deep+1); book[i] = 0; } } if (deep==10) {//十位数都放好了 String string = getString();//将全排列数组转成字符串 //接下来以0为标志,进行分割,0看成乘号,不能在首位 if (string.charAt(0)=='0' || string.charAt(9)=='0') {//charAt()方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1 return; } //对0进行分割,左边是乘数右边是被乘数 String[] split = string.split("0");//定义字符串数组split,split()方法根据匹配给定的正则表达式来拆分字符串;并储存在字符串数组中 int num1 = Integer.valueOf(split[0]);//"0"左边的是split[0] int num2 = Integer.valueOf(split[1]);//"0"右边的是split[1] if (check(num1*num2)) {//判断乘积是否由不同的数组成 System.out.println(num1 + " X " + num2 + " = " + num1*num2 );//如果由不同的组成,输出 count++;//计数 } return; } } public static void main(String[] args) { dfs(0); System.out.println(count/2);//因为被乘数和乘数位置互换算一种,所以除以2 } }
-
方格分割
6x6的方格,沿着格子的边线剪开成两部分。
要求这两部分的形状完全相同。public class Main { static int[][] a = new int[6][6]; static boolean[][] vis = new boolean[10][10]; static int[][] dir = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; static int cnt = 0; public static void main(String[] args) { vis[3][3] = true; f(3, 3); System.out.println(cnt/4); } private static void f(int x, int y) { // TODO Auto-generated method stub if (x == 0 || x == 6 || y == 0 || y == 6) { cnt++; return; } for (int i = 0; i < 4; i++) { int dx = x + dir[i][0]; int dy = y + dir[i][1]; // 找到一条线,可以分成两半 if (vis[dx][dy] == false) { vis[dx][dy] = true; vis[6-dx][6-dy] = true; f(dx, dy); vis[6-dx][6-dy] = false; vis[dx][dy] = false; } } } }
左右递归问题
- 李白打酒
话说大诗人李白,一生好饮。幸好他从不开车。
一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:
无事街上走,提壶去打酒。
逢店加一倍,遇花喝一斗。
这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。
请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目给出的)。
注意:通过浏览器提交答案。答案是个整数。不要书写任何多余的内容。public class libaidajiu2 { private static int counter; public static void main(String[] args) { counter = 0; libai(14, 2, 5, 10); System.out.println(counter); } //step表示遇到花和店的次数,又因最后一次必须是花,所以step=14,(即当step=0的时候,条件成立); // jiu表示还剩多少斗酒,dian表示还剩多少次店,hua表示还剩多少次花 private static void libai(int step, int jiu, int dian, int hua) { //递归出口 if (step < 0 || jiu < 0 || dian < 0 || hua < 1) { return; } //根据题意酒剩1斗,最后一次是花,则条件成立(即最后一次遇到花,把酒喝光) if (step == 0 && jiu == 1 && dian == 0 && hua == 1) { counter++; return; } libai(step - 1, jiu - 1, dian, hua - 1); // 遇到花了 libai(step - 1, jiu * 2, dian - 1, hua); // 遇到店了 } }
- 振兴中华
从我做起振
我做起振兴
做起振兴中
起振兴中华// 通过观察,当i == 3 || j == 4的时候,就可以算一种了,没有必要非得走到右下角 public class Main { public static void main(String[] args) { int ans = 0; ans = dfs(0, 0); System.out.println(ans); } public static int dfs(int i, int j) { if (i == 3 || j == 4) { return 1; } // dfs(i + 1, j):向下走的路线总数;dfs(i, j+ 1):向右走的路线总数 return dfs(i + 1, j) + dfs(i, j + 1); } }
暴力问题
-
牌型种数
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,自己手里能拿到的初始牌型组合一共有多少种呢?思路:循环遍历每个点数所选择的张数,每个点数最多可以选4张,最少可以选0张即不选,每当牌总数达到13张则计数。
#include <iostream> using namespace std; int main() { int sum=0; for(int a=0; a<=4; a++) for(int b=0; b<=4; b++) for(int c=0; c<=4; c++) for(int d=0; d<=4; d++) for(int e=0; e<=4; e++) for(int f=0; f<=4; f++) for(int g=0; g<=4; g++) for(int h=0; h<=4; h++) for(int i=0; i<=4; i++) for(int j=0; j<=4; j++) for(int k=0; k<=4; k++) for(int l=0; l<=4; l++) for(int m=0; m<=4; m++) { if(a+b+c+d+e+f+g+h+i+j+k+l+m==13) sum++; } cout<<sum<<endl; return 0; }
-
方格计数
半径为1000的圆里面有几个小正方形
public class Main { public static void main(String[] args) { // TODO Auto-generated method stub //求四分之一个象限 int a, b; int r = 1000;// 半径r int count = 0;// 方格个数 for (int i = 1; i < 1000; i++) { for (int j = 1; j < 1000; j++) { a = i ; b = j ; if (a * a + b * b <= r * r) count++; } } System.out.println(count * 4); } }
加上 + 递归 + 删除问题
- 牌型种数
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,自己手里能拿到的初始牌型组合一共有多少种呢?import java.util.*; public class Main { static int k[]={1,2,3,4,5,6,7,8,9,10,11,12,13}; static int count=0; public static void main(String args[]) { int t[]=new int[14]; //记录每个牌已经拿了几张 Get(0,0,t); // 每一张可以拿0,1,2,3,4张, // 第一个0代表1~13中的第一张的点数 第二个0代表拿到的总数 System.out.println(count); } public static void Get(int q,int w,int t[]) { if(w==13) //牌数够了 { count++;return; } if(q==13)return; //已经拿了最后点数位13的牌了 for(int i=0;i<=4;i++) { w+=i; t[q]=i; Get(q+1,w,t);//深度优先搜索 w-=i; //回溯法 } } }