[深搜回溯]24点

94 篇文章 4 订阅
74 篇文章 1 订阅

如果你是移动端,我推荐点击这个看链接看推送版的
推送版的链接

题目描述:
24点是一个有趣的扑克牌游戏。发4张牌,然后计算是否能够算出24点来。(不考虑有括号的算式,输出计算式将从左到有进行计算)
如果可以,输出算数表达式;
如果不可以,输出NONE
如果表达式中,有错误输入,输出“ERROR”
输入实例:
2
AAAA
Q3J8
输出实例:
NONE
Q-J*3*8

代码解析:
下面解析,将以对其中一组数据(4个字符)为例

  1. main函数中读入字符串
  2. 先选择第一个数
  3. 开始深搜,一直到搜了四个数之后,判断是否为24,是就将更新答案,并返回True
  4. 进行选择,每一次深搜,都是选择一个运算符和一个数字(字符)。用tempAns来记录表达式。
  5. 注意要对深搜部分进行恢复,避免影响下一次深搜。同时用visited记录访问过的点,避免重复使用,并进入死循环。(答案错了就检查是不是没有深搜恢复好;出现死循环,很有可能是因为没有标记访问过的点)
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;

char cards[4];
bool visited[4];
char cal[] = {'+', '-', '*', '/'};
string ans, tempAns;
double tempNum;
int charToNum(char c) {
	if (c <= '9' && c>= '0') {
		return c - '0';
	} else if (c == 'J') {
		return 11;
	} else if (c == 'Q') {
		return 12;
	} else if (c == 'K') {
		return 13;
	} else if (c == 'A') {
		return 1;
	}
	return -1;
}
bool DFS(int now) { // now From 1
	if(now == 4) {
		if (abs(tempNum - 24) < 1e-6) {
			ans = tempAns;
			return true;
		} 
		return false;
	}
	// choose a cal
	for (int i = 0; i < 4; ++i) {
		for (int j = 0; j < 4; ++j) {
			if (!visited[j]) {
				if (i == 0) {
					tempNum += charToNum(cards[j]);
					visited[j] = true;
					tempAns.push_back(cal[i]);
					tempAns.push_back(cards[j]);
				} else if (i == 1) {
					tempNum -= charToNum(cards[j]);
					visited[j] = true;
					tempAns.push_back(cal[i]);
					tempAns.push_back(cards[j]);
				} else if (i == 2) {
					tempNum *= charToNum(cards[j]);
					visited[j] = true;
					tempAns.push_back(cal[i]);
					tempAns.push_back(cards[j]);
				} else if (i == 3) {
					tempNum /= charToNum(cards[j]);
					visited[j] = true;
					tempAns.push_back(cal[i]);
					tempAns.push_back(cards[j]);
				}
				
				if(DFS(now + 1)) {
					return true;
				}
				
				if (i == 0) {
					tempNum -= charToNum(cards[j]);
					visited[j] = false;
					tempAns = tempAns.substr(0, tempAns.length() - 2);
				} else if (i == 1) {
					tempNum += charToNum(cards[j]);
					visited[j] = false;
					tempAns = tempAns.substr(0, tempAns.length() - 2);
				} else if (i == 2) {
					tempNum /= charToNum(cards[j]);
					visited[j] = false;
					tempAns = tempAns.substr(0, tempAns.length() - 2);
				} else if (i == 3) {
					tempNum *= charToNum(cards[j]);
					visited[j] = false;
					tempAns = tempAns.substr(0, tempAns.length() - 2);
				}
  			}
		}
	}
	return false;
}

int main(){
	int time, fa;
	cin >> time;
	while(time--) {
		char c;
		fa  = true;
		for (int i = 0; i < 4; ++i){
			cin >> c;
			if (c <= '9' && c>= '0' || c == 'J'  || c == 'Q' || c == 'K' || c == 'A') {
				cards[i] = c;
			} else {
				fa = false;
			}
		}
		if (!fa) {
			cout << "Error!\n"; continue;
		}
		memset(visited, false, sizeof(visited));
		
		bool ok = false;
		for (int i = 0; i < 4; ++i) {
			tempAns.clear();
			
			visited[i] = true;
			tempAns.push_back(cards[i]);
			
			tempNum = charToNum(cards[i]);
			if (DFS(1)){
				cout<< ans<< endl;
				ok =  true;
				break;
			}
			visited[i] = false;
		}
		if (!ok) {
			cout << "NONE\n";
		}
	}
}

类似需求的一个改进版本

  • 但任然不完善,没办法解决 1 5 5 5 这个数学式子的24点问题。
#include <iostream>
#include <stack>
#include <string>
#include <cmath>
using namespace std;

double arr[4];
struct pi{
	int v, op;
	/* 
	l: temp value
	r: each number in arr
	op:
		0: l + r
		1: l - r
		2: r - l
		3: l / r
		4: r / l
		5: l * r
	*/
	pi(int v_ = 0, int op_ = 0) {
		v = v_;
		op = op_;
	}
};
stack< pi > tmp_path;
bool visited[4];

bool DFS(int temp, int time) {
	if (time == 4) { return abs(temp - 24) < 1e-6; }
	double value;
	for (int i = 1; i < 4; ++i) {
		if (visited[i]) continue;
		else visited[i] = true;
		value = arr[i];
		// op:0, temp + value
		tmp_path.push(pi(value, 0));
		if (DFS(temp + value, time + 1))
			return true;
		tmp_path.pop();

		// op:1, temp - value
		tmp_path.push(pi(value, 1));
		if (DFS(temp - value, time + 1))
			return true;
		tmp_path.pop();

		// op:2, value - temp 
		tmp_path.push(pi(value, 2));
		if (DFS(value - temp, time + 1))
			return true;
		tmp_path.pop();

		// op:3, temp / value
		tmp_path.push(pi(value, 3));
		if (DFS(temp / value, time + 1))
			return true;
		tmp_path.pop();

		// op:4, value - temp 
		tmp_path.push(pi(value, 4));
		if (DFS(value / temp, time + 1)) 
			return true;
		tmp_path.pop();

		// op:5, temp * value
		tmp_path.push(pi(value, 5));
		if (DFS(temp * value, time + 1)) 
			return true;
		tmp_path.pop();

		visited[i] = false;
	}
	
	return false;
}

string returnCalStr(int time) {
	string s;
	string op_str;
	if (time == 3) {
		s = to_string(int(arr[0]));
		return s;
	}
	else {
		pi t_pi = tmp_path.top();
		s = to_string(int(t_pi.v));
		tmp_path.pop();
		string tmp = returnCalStr(time+1);
		
		
		if (t_pi.op == 0) {
			op_str = string("+");
		}
		else if (t_pi.op == 1 || t_pi.op == 2) {
			op_str = string("-");
		}
		else if (t_pi.op == 3 || t_pi.op == 4) {
			op_str = string("/");
		}
		else op_str = string("*");
		
		if (t_pi.op == 2 || t_pi.op == 4) {
			return string("(") + s + op_str + tmp + string(")");
		}
		else {
			return string("(") + tmp + op_str + s + string(")");
		}		
	}
}

void print_path() {
	cout << returnCalStr(0) << endl;
}

int main() {
	int time;
	cin >> time;
	
	while (time--) {
		for (int i = 0; i < 4; ++i) {
			cin >> arr[i];
		}
		
		for (int i = 0; i < 4; ++i) visited[i] = false;
		visited[0] = true;
		if (DFS(arr[0], 1)) {
			print_path();
		}
		else {
			cout << "None\n";
		}
	}
	system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥宅_Sean

公众号“肥宅Sean”欢迎关注

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值