• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
百事可爱
一起努力鸭~~~
博客园    首页    新随笔    联系   管理    订阅  订阅
枚举法

枚举法

枚举算法解题的基本思路:

  1. 确定枚举解的范围,以及判断条件
  2. 选取合适枚举方法,进行逐一枚举,此时应注意能否覆盖所有的可能的解,同时避免重复。
  3. 在枚举时使用判断条件检验,留下所有符合要求的解。

1. 简单型枚举

就是可以通过简单的 for 循环嵌套就可以解决的问题

42 点问题

题目描述:

众所周知在扑克牌中,有一个老掉牙的游戏叫做24点,选取4张牌进行加减乘除,看是否能得出24这个答案。 现在小蓝同学发明了一个新游戏,他**从扑克牌中依次抽出6张牌,注意不是一次抽出,进行计算,看是否能够组成 42 点,满足输出YES,反之输出 NO。 最先抽出来的牌作为第一个操作数,抽出牌做第二个操作数,运算结果在当作第一个操作数,继续进行操作。 除不尽的情况保留整数。 **请设计一个程序对该问题进行解答。

样例:

输入:

K A Q 6 2 3  

输出:

YES

对于上面的样例我们进行了如下计算;

1. K*A=K 即 13*1=13
2. 13/12=1 保留整数
3. 1+6=7
4. 7*2=14
5. 14*3=42

普及扑克牌常识:

一副扑克牌共有54张牌,其中52张是正牌,另2张是副牌(大王和小王)。

52张正牌又均分为13张一组,并 黑桃 ,红桃 、 梅花 、方块四种花色表示各组,每组花色的牌包括从1-10( 1通常表示为A) 以及J、Q、K标示的13张牌

对应此题:J、Q、K代表数字11,12,13;就是1到13各有4张

题目解析:

可以依次枚举数字,然后在枚举数字间的符号即可;

创建 5 个 Vector ,分别用来存放 1-5 次的运算结果;

op1 赋值为 第一个数

op(op[1] op[2])
{

    for op in [+ - * /]
       ans = 第一个操作数op1 操作 第二个操作数op2
    
        如果是第六个操作数,就检验是否符合要要求 ==42? 如果是就返回True

        如果op(ans , op[3]) 返回 True,就返回True 因为找到了答案,否则就继续进行

    没有找到答案返回False
}

代码实现(此题还是没有搞明白??)

package 枚举法;
 
import java.util.Scanner;
import java.util.Vector;
//依次抽出六张牌,进行五次运算(可以是 + - * / 中的一种) 看最后的结果是否为42
//创建 5 个 Vector ,分别用来存放 1-5 次的运算结果
public class SiShiErDian {

	static int[] a = new int[10];
	
	static Vector<Vector<Integer>> ans = new Vector<>();

	public static void main(String[] args) {

		Scanner in = new Scanner(System.in);

		for (int i = 0; i < 6; i++) {//循环实现抽六张牌,并将其对应的数字放到了a数组中保存
			
			String c = in.next();
		 //调用String类的  char charAt(int index) 返回指定的在字符串中下标为index出的字符
			if (c.charAt(0) == 'A')
				a[i] = 1;

			else if (c.charAt(0) == 'J')
				a[i] = 11;

			else if (c.charAt(0) == 'Q')
				a[i] = 12;

			else if (c.charAt(0) == 'K')
				a[i] = 13;

			else
				a[i] = (c.charAt(0) - '0');

		}
		
		ans.addElement(new Vector<Integer>());//void java.util.Vector.addElement(Vector<Integer> obj)
		ans.get(0).addElement(a[0]);

		
		for (int i = 1; i <= 5; i++) {			
			ans.addElement(new Vector<Integer>());
			for (int j = 0; j < ans.get(i - 1).size(); j++) {
				ans.get(i).addElement(ans.get(i - 1).get(j) + a[i]);

				ans.get(i).addElement(ans.get(i - 1).get(j) - a[i]);

				ans.get(i).addElement(ans.get(i - 1).get(j) * a[i]);

				ans.get(i).addElement(ans.get(i - 1).get(j) / a[i]);
			}
		}
/*K A Q 6 2 3*/
		 
		int flag = 0;

		for (int i = 0; i < ans.get(5).size(); i++) {
			if (ans.get(5).get(i) == 42) {
				flag = 1;
				break;
			}
		}

		if (flag == 1)
			System.out.println("YES");

		else
			System.out.println(" NO");
	}
}
/*1. public class Vector<E> implements List<E>  List集合中存储元素特点:有序可重复(有下标),有序指存进去的顺序与取出时相同
 *2. Vector集合底层是一个数组,初始化容量是10
 *3. public Vector(Collection<? extends E> c) {
        Object[] a = c.toArray();
        elementCount = a.length;
        }
        
   4. public synchronized void addElement(E obj) {
        modCount++;
        add(obj, elementData, elementCount);
    }*/
 

2. 组合型枚举(参考递归实现组合算法)

就是排列组合, 就是让你在 n 个中,随机选出 m 个,问你有多少种方案,而且每一种方案选择了哪 m 个,这就是组合型枚举。
组合型枚举有固定的流程,即有着固定的算法模板:(以下是给定的数字中没有重复项进行组合)

import java.util.ArrayList;
import java.util.List;

public class ZuHe {

	 //暂时存储组合的一种情况
	private static List<Integer> outArr = new ArrayList<Integer>();
	/*outArr中存放的数量*/ 
	private static int index=0;
	
	/**
	 * 
	 * @param inArr 待组合的数组
	 * @param n 表示inArr位置,如n=0则从inArr[0]到inArr[length-1]选取数字;n=1,则从inArr[1]到inArr[length-1]选取数字,length为数组长度
	 * @param num 要选出数字的数量
	 */
	public static void comNum(int[] inArr,int n,int num) {
		if(num<=0) { //num=0 递归出口
			//打印数组
			for(int i:outArr) {
				System.out.print(i);
				}
			System.out.println();
		}else {
			for(int i=n;i<=inArr.length-num;i++) {
				if(outArr.size()<index+1) {
					outArr.add(index++,inArr[i]);
					} /*此处两行为防止集合越界空指针异常,如果使用数组存放也可,即此处实现了选取一个元素放进输出数组里,对应上述文字说明2的每一步的a操作*/
				else {
					outArr.set(index++, inArr[i]);
				}
				comNum(inArr,i+1,num-1); /*在选取的元素后面的元素中选取num-1个元素,对应文字说明2中每一步的b操作*/
				index--; //重新给输出数组定位,选取一下个序号的元素 就是选择下一个放在第一位的数字
			}
		}
	}
public static void main(String[] arg) {
        int[] arr = {1,2,3,4,5};
        comNum(arr,0,2);
    }
}
/*
12
13
14
15
23
24
25
34
35
45
*/

例题:

题目描述:

小A的学校,蓝桥杯的参赛名额非常有限,只有m个名额,但是共有n个人报名其中m<=n。,作为老师非常苦恼,他不知道该让谁去,他在寻求一个绝对公平的方式。于是他准备让大家抽签决定,即m个签是去,剩下的是不去。 小A非常想弄明白最后的抽签结果是什么样子的,到底有多少种结果。 请设计一个程序帮助小A。最后输出各种情况的人名即可,一行一种情况,每种情况的名字按照报名即输入顺序排序。 第一行 输入 N M 第二行 到 第 N+1 行 共输入 N 个人名 每种情况输出 M 个人名,空格隔开。

样例:

输入: 3 2

xiaowang

xiaoA

xiaoli

输出:

xiaowang xiaoA

xiaowang xiaoli

xiaoA xiaoli

题目解析:

实际上还是组合型枚举,但是输出元素为人名,我们可以将人名存起来,输出的时候,根据数字下标找到提前存好的人名,直接输出即可。

代码实现:

package 枚举法;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class ZuHe3 {

	// 暂时存储组合的一种情况
	static List<Integer> chosen = new ArrayList<Integer>();
	// 存储组合的下标/人数
	static int index = 0;

	// 存放人名
	static List<String> name = new ArrayList<>();
	//
	static List<Integer> nameIndex = new ArrayList<>();

	static int n;
	static int m;// 选m个数

	public static void comNum(List<Integer> nameIndex, int n, int m) {
		if (m <= 0) { // num=0 递归出口
			for(int i=0;i<chosen.size();i++) {
				System.out.print(name.get(chosen.get(i))+" ");
			}
			System.out.println();
		} else {
			for (int i = n; i <= nameIndex.size() - m; i++) {
				if (chosen.size() < index + 1) {
					chosen.add(index++, nameIndex.get(i));
				} /* 此处两行为防止集合越界空指针异常,如果使用数组存放也可,即此处实现了选取一个元素放进输出数组里 */
				else {
					chosen.set(index++, nameIndex.get(i));
				}
				comNum(nameIndex, i + 1, m - 1); /* 在选取的元素后面的元素中选取num-1个元素 */
				index--; // 重新给输出数组定位,就是选择下一个放在第一位的数字
			}
		}
	}

	public static void main(String[] args) {

		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		m = in.nextInt();

		// 将所有人名保存
		for (int i = 0; i < n; i++) {
			String string = in.next();
			name.add(string);
		}

		for (int i = 0; i < name.size(); i++) {
			nameIndex.add(i);
		}
		comNum(nameIndex, 0, 2);
	}
/*
3  2
xiaowang
xiaoA
xiaoli
xiaowang xiaoA 
xiaowang xiaoli 
xiaoA xiaoli 
*/
}

3. 排列型枚举(参考全排列算法)

排列型枚举,就是 n 个的全排列,即从 n 个中选取 n 个但是关心内部的顺序。
相比较组合只关心有多少个集合,而排列是关心集合内的排列方式。

package 枚举法;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

//实现从n个数字里面取n个数字进行全排列(此时,n个数字不重复)
public class PaiLie {

	static int count = 0;// 统计种数
	// 存放要排列的全部元素
	static List<Integer> list1 = new ArrayList<Integer>();
	// 存放每一种排列的情况
	static List<Integer> list2 = new ArrayList<Integer>();

	public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		
		// 使用List2集合保存每次取出的一种排列,但不是直接add添加,先将集合的长度进行固定,要通过添加元素的方式固定
		for(int i = 0;i<n;i++)
		{
			list2.add(-1);
		} // 实现集合的长度固定为n
		
		//初始化要排列的数字
		for(int i=0;i<n;i++) {
			int number = scanner.nextInt();
			list1.add(number);
		}
		
		sortAll(list2, list1, n);
		System.out.println("共有"+count+"种排列方式");
	}
	
	//方法实现全排列
	/**
	 * @param list2  存放要排列的全部元素
	 * @param list1  存放每一种排列的情况
	 * @param n      要排列的数字个数
	 */
	public static void sortAll(List<Integer> list2, List<Integer> list1, int n) {
		// 出口
		if (n == 0) {// 取出其中一种一种情况的排列组合
			System.out.println(list2);
			count++;
			return;
		}

		for (int i = 0; i < list1.size(); i++) {
			if (!list2.contains(list1.get(i))){// 将数组中的元素依次取出,若集合中不包含此元素,就添加到集合中
				list2.set(list2.size() - n,list1.get(i));// set(下标,元素) 若n=3, 则list.size()-n=3-3=0
			} else {
				continue;
			}
			sortAll(list2, list1, n - 1);// 更新n
			list2.set(list2.size() - n, -1);// 还原 特别注意:原来集合中只有n位-1
		}
	}
}
/*输出结果:
3
1
2
3
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
共有6种排列方式
*/

例题:

题目描述:

小 A 的学校,老师好不容易解决了蓝桥杯的报名问题,现在老师又犯愁了。现在有 N 位同学参加比赛,但是老师想给他们排座位,但是排列方式太多了。老师非常想弄明白最后的排座次的结果是什么样子的,到底有多少种结果。 请设计一个程序帮助老师。 最后输出各种情况的人名即可,一行一种情况,每种情况的名字按照报名即输入顺序排序。 第一行 输入 N; 第二行 到 第N+1 行 共输入 N 个人名。 由于小 A 学校承办能力实在有限,所以其中 N 小于等于 10 人。

样例:

输入:

3

xiaowang

xiaoA

xiaoli

输出:

xiaowang xiaoA xiaoli

xiaowang xiaoli xiaoA

xiaoA xiaowang xiaoli

xiaoA xiaoli xiaowang

xiaoli xiaowang xiaoA

xiaoli xiaoA xiaowang

题目解析:

实际上还是排列型枚举,但是输出元素为人名,我们可以将人名存起来,输出的时候,根据数字下标找到提前存好的人名;

代码实现:

package 枚举法;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class PaiLie2 {
	static int count = 0;// 统计种数
	// 存放要排列的全部元素
	static List<Integer> list1 = new ArrayList<Integer>();
	// 存放每一种排列的情况
	static List<Integer> list2 = new ArrayList<Integer>();
	//存放所有同学的姓名
	static List<String> name = new ArrayList<>();
	
	public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		
		// 使用List2集合保存每次取出的一种排列,但不是直接add添加,先将集合的长度进行固定,要通过添加元素的方式固定
		for(int i = 0;i<n;i++)
		{
			list2.add(-1);
		} // 实现集合的长度固定为n

		//初始化名字集合
		for(int i=0;i<n;i++) {
			 String string = scanner.next();
			 name.add(string);
		}
		//初始化要排列的名字所对应的下标,实际上先对各个下标进行排列,最后再输出对应的名字即可。
		for(int i=0;i<name.size();i++) {
			list1.add(i);
		}
		
		
		sortAll(list2, list1, n);
		System.out.println("共有"+count+"种排列方式");
	}
	
	//方法实现全排列
	/**
	 * @param list2  存放要排列的全部元素
	 * @param list1  存放每一种排列的情况
	 * @param n      要排列的数字个数
	 */
	public static void sortAll(List<Integer> list2, List<Integer> list1, int n) {
		// 出口
		if (n == 0) {// 取出其中一种情况的排列组合
			 for(int i=0;i<list2.size();i++) {
				 System.out.print(name.get(list2.get(i))+" ");
			 }
			 System.out.println();
			count++;
			return;
		}

		for (int i = 0; i < list1.size(); i++) {
			if (!list2.contains(list1.get(i))){// 将数组中的元素依次取出,若集合中不包含此元素,就添加到集合中
				list2.set(list2.size() - n,list1.get(i));// set(下标,元素) 若n=3, 则list.size()-n=3-3=0
			} else {
				continue;
			}
			sortAll(list2, list1, n - 1);// 更新n
			list2.set(list2.size() - n, -1);// 还原 特别注意:原来集合中只有n位-1
		}
	}
}
/*结果测验:
 3
xiaowang
xiaoA
xiaoli
xiaowang xiaoA xiaoli 
xiaowang xiaoli xiaoA 
xiaoA xiaowang xiaoli 
xiaoA xiaoli xiaowang 
xiaoli xiaowang xiaoA 
xiaoli xiaoA xiaowang 
共有6种排列方式
*/
 */
posted on 2022-03-22 11:01  精致猪猪侠  阅读(941)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3