总结——常见套路

本文探讨了多个编程问题,涉及全排列在解决数学问题中的应用,如:100的带分数表示、六角填数、九数分组、寒假作业中的算术操作、剪邮票的排列、9数算式的乘法全排列以及方格分割等。每个问题都通过递归和回溯等算法寻找解决方案,展示了计算机如何帮助我们解决复杂数学问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

全排列问题

  • 代分数

    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;      //回溯法     
    		}
    	}
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值