24点递归理解起来确实难,基本要求做不出来,只好看别人做的
原作者:https://blog.csdn.net/qq_36691353/article/details/82949813
题目:
24点游戏是经典的纸牌益智游戏。
常见游戏规则:
从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求编程解决24点游戏。
基本:
随机生成4个代表扑克牌牌面的数字字母,程序自动列出所有可能算出24的表达式且表达式无重复。
提高:
除此之外,我们还可以多加一点功能让它变成一个游戏,比如我们随机给出四张牌并给玩家设置一个生命值,让其在一定的时间内组成表达式,如果值为24,则加分,答错则扣分和扣生命值!最后将成绩存在本地文件!
基本(列出所有值为24的表达式):
这里是用递归实现的(思想):
(1)在四个数中取俩个(例如:第一个,第二个)进行计算(遍历每一种情况)
(2)计算结果
(3)将结果放在数组中的第一个位置,并将空余出来的位置(第二个)存放最后一个数字,形成一个新数组
(4)递归调用函数计算找到表达式返回
(5)将步骤2的结果移除,并将该组合中的两个数重新放回该数组,进行下一个循环遍历另外俩个数继续计算
Java源码:
package zy;
import java.util.Random;
public class Found {
static int n = 4;//运算次数标记
static int[] A = new int[4];//存放四个数字
static String[] B = new String[4];//存放字符串型的四个数字,也存放表达式
static int count = 0;//计数
public static void f(int n) {
//判断是否已完成三次运算
if(n == 1)
if(A[0] == 24) {//A[0]里面存放的最后的结果
System.out.println(B[0]);//B[0]存放的结果表达式
count++;
}
else {//空的不做处理进行下一条语句
}
//从数组中任意取出俩个数的组合
for(int i = 0;i < n;i++) {
for(int j = i + 1;j < n;j++) {
int a,b;
String x,y;
a = A[i];
b = A[j];
A[j] = A[n-1];//将最后一位的数字放入下标为j的位置(也就是空出来的那个位置,相当于空出来,)
x = B[i];
y = B[j];
B[j] = B[n-1];//与上面同理
A[i] = a + b;//第一个空间保存运算的结果
B[i] = '(' + x + '+' + y + ')';//将第一步运算的表达式放在字符串数组中
f(n-1);
//减法需要区分顺序
A[i] = a - b;
B[i] = '(' + x + '-' + y + ')';
f(n-1);
A[i] = b - a;
B[i] = '(' + y + '-' + x + ')';
f(n-1);
//乘法
A[i] = a * b;
B[i] = '(' + x + '*' + y + ')';
f(n-1);
//除法和减法一样需区分顺序这里要处理除数为0的情况,因为计算过程中可能出现除数为0的情况
if(b!=0) {
A[i] = a / b;
B[i] = '(' + x + '/' + y + ')';
f(n-1);
}
if(a!=0) {
A[i] = b / a;
B[i] = '(' + y + '/' + x + ')';
f(n-1);
}
//当上面四则运算都不满足时
//为了方便进入下一个for循环, 需要将之前的i和j上的值都重新找回
A[i] = a;
A[j] = b;
B[i] = x;
B[j] = y;
}
}
}
public static void getnumber() {
Random r = new Random();
for(int i = 0;i < 4;i++) {
A[i] = r.nextInt(13) + 1;//产生1-13之间的数
}
for(int i = 0;i < 4;i++) {
if(A[i] == 1)
B[i] = "A";
else if(A[i] == 11)
B[i] = "J";
else if(A[i] == 12)
B[i] = "Q";
else if(A[i] == 13)
B[i] = "K";
else
B[i] = String.valueOf(A[i]);
}
System.out.println("产生的随机牌为以下四张:");
for(int i = 0;i < 4;i++) {
System.out.println(B[i]);
}
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
getnumber();//得到随机数
f(n); //调用函数求解
System.out.println("共有"+count+"种解法");
}
}
结果截图:
总结:
(1)真的意识到了递归的强大,我开始想到的是穷举,但是代码太冗杂了,尝试着在网上找到递归方法学习一下,然后就看不懂,最后在本子上带入算了一番,才算模糊有点感觉了,果然还是太笨了吗?哈哈
(2)虽然笨了点,但是还是对递归加深了一点理解!
(3)在调试途中收获了一点知识!写了另一篇博客
字符加数字会产生什么(有点low)
提高(将24点游戏真的做成一个简单游戏):
思路:
利用栈来判断用户输入的表达式是否等于24
计时功能用多线程实现(不完善:新线程一直运行会到第二题的开始,没有解决,期待大佬的指点)
求值:
使用中缀表达式求值的,所以括号必须平衡并且表达式完全括号化(太low了)
什么是完全括号化?比如:
4+4+10+6
这个表达式需要写成:
(((4+4)+6)+10)
也就是3对括号必须有。。。
Java源码:
package zy;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
public class Yx extends Thread{
static int HP = 3;//用户初始生命值
static int CORE = 0;//用户初始成绩
static boolean t = true;
public void run() {
if(t=true) {
int time = 0;
while(++time<=6){
try{
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
HP = HP - 1;
System.out.println("时间到!当前你的生命值为:"+HP+" "+"得分为:"+CORE);
t = false;
}
else
return;
}
//用来取出栈中的俩个元素进行计算的函数
public static void evaluateStackTops(Stack<Integer> numbers,Stack<Character> operations) {
int operand1,operand2;
if(numbers.size()<2||operations.isEmpty()) {
System.out.println("表达式错误!");
return;
}
operand2 = numbers.pop(); //先取出来的数给与右操作数,因为计算除法和减法的时候是用更下一层的数做被除数和被减数的
operand1 = numbers.pop();
switch(operations.pop()) {
case '+':
numbers.push(operand1+operand2);
break;
case '-':
numbers.push(operand1-operand2);
break;
case '*':
numbers.push(operand1*operand2);
break;
case '/':
numbers.push(operand1/operand2);
break;
default:
System.out.println("表达式错误!");
return;
}
}
//实现计算功能,参数必须是一个完全括号化的表达式,所以用到了括号的平衡判断
public static int evaluate(String expression) {
Stack<Integer> numbers = new Stack<Integer>();
Stack<Character> operations = new Stack<Character>();
//将表达式转化为一个Scanner 类对象好操作
//字符串next中存放表达式下一个字段,比如数值或运算符
Scanner input = new Scanner(expression);
char next;
while(input.hasNext()) { //当字符串有下一个字符时
if(input.hasNextInt()) {//如果下一个是数字压栈
numbers.push((input.nextInt()));
}
else {
next = input.next().charAt(0);
switch(next) {
case '+'://加法
case '-'://
case '*'://
case '/'://
operations.push(next);
break;
case ')':
evaluateStackTops(numbers,operations);//计算
break;
case '(':
break;
default://其他字符
System.out.println("表达式错误!");
return 0;
}
}
}
input.close();
if(numbers.size()!=1){
System.out.println("表达式错误!");
return 0;
}
return numbers.pop(); //栈中最后一个元素就是表达式的值
}
public static void game(){
if(HP==0) {//终止条件
writer();
System.out.println("游戏结束!");
return;
}
Scanner in = new Scanner(System.in);
Random r = new Random();
String s = null;
int[] a = new int[4]; //存放随机数
for(int i = 0;i<a.length;i++)
a[i] = r.nextInt(13)+1; //随机产生1-13个数
System.out.println("你获得的随机数是:");
for(int i:a)
System.out.println(i);
System.out.println("请动用你的聪明才智在60秒内将它组成一个表达式且值为24!(表达式需要完全括号化)");
System.out.println("友情提示:每个字符之间请用空格隔开,否则会出错");
Yx t = new Yx();
t.start();
s = in.next();
if(evaluate(s)==24) {
System.out.println("可以呀答对了,+10分");
CORE = CORE + 10;
System.out.println("当前你的生命值为:"+HP+" "+"得分为:"+CORE);
}
else {
System.out.println("答案错误,-10分");
CORE = CORE - 10;
HP = HP - 1;
System.out.println("当前你的生命值为:"+HP+" "+"得分为:"+CORE);
}
}
public static void writer() {
File f = new File("TopList.txt");
try {
FileWriter out = new FileWriter(f);
String s = Integer.toString(CORE);
out.write(s);
out.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
public static void reader() {
File f = new File("TopList.txt");
if(!f.exists()) {
System.out.println("请先参与游戏,否则没有成绩!");
return;
}
try {
FileReader in = new FileReader(f);
char[] r = new char[1024];
int len = in.read(r);
String s = new String(r,0,len);
System.out.println("你的成绩为:"+s);
in.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
public static void menu() {
System.out.println("**********************");
System.out.println("1.开始游戏\n2.查看成绩");
Scanner in = new Scanner(System.in);
int t = in.nextInt();
switch(t) {
case 1:
while(HP>0) {
game();
}
writer();
System.exit(0);
break;
case 2:
reader();
break;
}
in.close();
}
public static void main(String[] args){
menu();
}
}
总结:
(1)了解到了Java中的栈的用法以及其封装的方法–>pop()出栈,peek()读取栈顶元素但不出栈,push()压栈
(2)了解了多线程的基础用法,认识到了多线程的“恐怖”,如果不好好处理,它会一直运行。
(3)了解了文件的读写。