计算24点是一种扑克牌益智游戏,随机抽出4张扑克牌,通过加(+),减(-),乘(*), 除(/)四种运算法则计算得到整数24,本问题中,扑克牌通过如下字符或者字符串表示:
A 2 3 4 5 6 7 8 9 10 J Q K
程序要求实现:输入4张牌,判断能否通过四则运算得到24点。
在网上没找到什么好的算法,最后选择了暴力遍历,思路如下:
字母到数字的转换比较简单,这里不做介绍,并认为输入的数据为数字,即摸到K的时候输出13
- 先从4个数中任选2个数做±/,得到结果集A
如:输入4个数(1 2 3 4),取其中的1,2做四则运算,得到A为
(1-2,1+2,12,1/2)->(-1,3,2,0.5) - 遍历A,与step1中剩余两个数组成一个长度为A.size()的List记为ret4
遍历A,与剩余两个数组合,得到ret4为(-1,3,4),(3,3,4),(0.5,3,4)…… - 类似的,遍历ret4,从3个数中,取任意两个数做±*/,得到结果集B
- 遍历结果集B,与step3中剩余的一个数组成长度为B.size()的List记为ret3
- 遍历ret3,对这两个数做±*/操作,得到的结果集存入ret2
- 若ret2.contains(24)则可以得到24点,否则不能
下面上代码:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* @Author Jeb
* @Date 2020/4/3
*/
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
// 读取四个数存入inl中
List<Double> inl = new ArrayList<>();
for (int i = 0; i < 4; i++) {
inl.add(sc.nextDouble());
}
//四个数任取两个做+-*/运算,运算结果与剩余两个数组成一个3个数的List,将这些List存入ret4中
//如1 2 3 4,取2和4做运算,则得到一个{{1,3,2+4},{1,3,2-4},{1,3,2*4},{1,3,2/4}}的集合
List<List<Double>> ret4 = new ArrayList<>();
//三个数任取两个做+-*/运算,运算结果与剩余一个数组成一个2个数的List,将这些两个数的List存入ret3中
List<List<Double>> ret3 = new ArrayList<>();
//对两个数做+-*/运算,运算结果存入ret2中
List<Double> ret2 = new ArrayList<>();
cal4(inl, ret4);
cal3(ret4,ret3);
cal2(ret3,ret2);
System.out.println(ret2.contains(new Double("24")));
}
}
/**
* 输入一个长度为4的List,任取两个数做+ - * /运算,结果与剩余两个数组成3个数的List存入ret中
* @param inl 4个数字组成的List<Double>
* @param ret 3个数组成的List<Double>的集合
*/
public static void cal4(List<Double> inl,List<List<Double>> ret) {
//遍历输入inl,以实现取任意一个数的情况
for (int i = 0; i < inl.size(); i++) {
//取遍历值
Double l = inl.get(i);
Double r;
//以inl为基础建立一个副本
List<Double> tmp = new ArrayList<>(inl);
//删除已经出去的元素,得到剩余三个元素的集合
//如inl:{1,2,3,4},取出1后,得到一个剩余三个元素的tmp:{2,3,4}
tmp.remove(inl.get(i));
//遍历tmp以实现取剩余三个数中任意一个数的情况
for (int j = 0; j < tmp.size(); j++) {
r = tmp.get(j);
//以tmp为基础建立一个副本
List<Double> tmp1 = new ArrayList<>(tmp);
//删除已经取出的元素,得到剩余两个元素的集合
//如tmp:{2,3,4},取出2后,得到一个剩余2个元素的tmp1:{3,4}
tmp1.remove(r);
//l,r做+-*/四则运算后存入asmdList
List<Double> asmdList = asmd(l,r);
// 遍历asmdList
for(Double asmd:asmdList) {
//分别将每个四则运算结果加入tmp1中,组成一个3个元素的List
tmp1.add(asmd);
//将3个元素的List添加到ret中
ret.add(new ArrayList<>(tmp1));
//还原tmp1到未添加四则运算结果时的状态,以便下一循环的添加
tmp1.remove(asmd);
}
}
}
}
/**
* 输入一个长度为3的List,任取两个数做+ - * /运算,结果与剩余一个数组成3个数的List存入ret中
* @param inl 3个数字组成的List<Double>的集合
* @param ret 2个数组成的List<Double>的集合
*/
public static void cal3(List<List<Double>> inl,List<List<Double>> ret) {
for(List<Double> singlel : inl) {
for (int i = 0; i < singlel.size(); i++) {
Double l = singlel.get(i);
Double r;
List<Double> tmp = new ArrayList<>(singlel);
tmp.remove(singlel.get(i));
for (int j = 0; j < tmp.size(); j++) {
r = tmp.get(j);
List<Double> tmp1 = new ArrayList<>(tmp);
tmp1.remove(r);
//l,r做+-*/四则运算后存入t
List<Double> asmdList = asmd(l,r);
for(Double asmd:asmdList) {
tmp1.add(asmd);
ret.add(new ArrayList<>(tmp1));
tmp1.remove(asmd);
}
}
}
}
}
/**
* 输入一个长度为2的List,任取两个数做+ - * /运算,结果存入ret中
* @param inl 2个数字组成的List<Double>的集合
* @param ret 两个数做+ - * /的结果集合
*/
public static void cal2(List<List<Double>> inl,List<Double> ret) {
for(List<Double> singlel : inl) {
ret.addAll(asmd(singlel.get(0),singlel.get(1)));
ret.addAll(asmd(singlel.get(1),singlel.get(0)));
}
}
public static List<Double> asmd(Double l,Double r) {
List<Double> ret = new ArrayList<>();
for (int k = 0; k < 4; k++) {
switch (k) {
case 0:
ret.add(l + r);
break;
case 1:
ret.add(l - r);
break;
case 2:
ret.add(l * r);
break;
case 3:
if (r != 0) {
ret.add(l / r);
}
break;
default:
break;
}
}
return ret;
}
}