1789:算24 (Java DFS 5分原因看过来)

题目链接
在这里插入图片描述
在这里插入图片描述
可能做法比较麻烦,就当深搜的练习吧。分两步,第一步枚举所有的可能的运算式,第二步计算每个运算式所有可能的结果,此结果就是任意位置加括号的所有可能结果,与这个力扣题目相似。
注意题目说的是这4个数字任意组合,一开始还固定了顺序,只在数字间枚举算符。
如果提交之后得了5分,原因应该是double数据的比较存在误差,只要计算结果与24的差值小于0.0001就认为表达式可以得到24.
还有要注意的是每个测试样例用的公用数据结构如vis,exps一定要重新初始化或清空,对于mp映射一个子表达式可能的结果,这个对于所有测试样例可以公用,但是也要清空,否则会内存超限制。

import java.util.*;

public class Main{
	//拆分成两步来做
	//第一步dfs出所有可能的表达式
	//第二步对各个表达式分别求所有可能的运算结果(从而体现在不同的位置添加括号改变运算顺序)
	public static int [] nums=new int [4];
	public static Set<String> exps=new HashSet<String>();
	public static boolean[] vis=new boolean[4];
	public static void dfs(int cnt,String exp) {
		for(int i=0;i<4;i++) {
			if(!vis[i]) {
				vis[i]=true;
				if(cnt==3) {
					exps.add(exp+String.valueOf(nums[i]));
					vis[i]=false;
					return;
				}
				dfs(cnt+1,exp+String.valueOf(nums[i])+"+");
				dfs(cnt+1,exp+String.valueOf(nums[i])+"-");
				dfs(cnt+1,exp+String.valueOf(nums[i])+"*");
				dfs(cnt+1,exp+String.valueOf(nums[i])+"/");
				vis[i]=false;
			}
		}
	}

	//记忆化搜索,所有表达式的值都可以从这里面获取,因为相同子表达式的值的结果是固定的
	//还是每次都清空吧,否则可能内存超限制
	public static Map<String,LinkedList<Double>> mp=new HashMap<String,LinkedList<Double>>();
	public static LinkedList<Double> dfs2(String curExp) {
		LinkedList<Double> tmp=new LinkedList<Double>();
		if(curExp.equals("")) {
			return tmp;
		}else if(mp.containsKey(curExp)) {
			return mp.get(curExp);
		}else if(curExp.length()==1 && Character.isDigit(curExp.charAt(0))) {
			tmp.addLast(Double.parseDouble(curExp));
			mp.put(curExp, tmp);
			return tmp;
		}
		for(int i=0;i<curExp.length();i++) {
			if(!Character.isDigit(curExp.charAt(i))) {
				LinkedList<Double> a,b;
				a=dfs2(curExp.substring(0, i));
				b=dfs2(curExp.substring(i+1));
				for(int m=0;m<a.size();m++) {
					for(int n=0;n<b.size();n++) {
						if(curExp.charAt(i)=='+') {
							tmp.addLast(a.get(m)+b.get(n));
						}else if(curExp.charAt(i)=='-') {
							tmp.addLast(a.get(m)-b.get(n));
						}else if(curExp.charAt(i)=='*') {
							tmp.addLast(a.get(m)*b.get(n));
						}else {
							tmp.addLast(a.get(m)/b.get(n));
						}
					}
				}
				
			}
		}
		mp.put(curExp, tmp);
		return tmp;
	}
	
	public static void main(String[] Args) {
		Scanner sc=new Scanner(System.in);
		while(sc.hasNextInt()) {
			for(int i=0;i<4;i++) {
				nums[i]=sc.nextInt();
			}
			if(nums[0]==0) {
				sc.close();
				break;//输入都是正整数,所以有一个0就是终止了
			}
			exps.clear();//需要清空,否则前面的表达式会一直存着
			mp.clear();
			Arrays.fill(vis, false);
			dfs(0,"");//第一个dfs生成所有可能的表达式 
			boolean flag=false;
//			for(int i=0;i<exps.size();i++) {
//				System.out.println(exps.get(i));
//			}
			for(String str:exps) {
				dfs2(str);//第二个dfs检查各个表达式所有可能的计算结果
				LinkedList<Double> e=mp.get(str);
				for(double item:e) {
					if(Math.abs(item-24.0)<0.0001) {
						flag=true;
						break;
					}
				}
				if(flag) {
					break;
				}
			}
			if(flag) {
				System.out.println("YES");
			}else {
				System.out.println("NO");
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值