挑战 OpenJudge 百练第 2787 题:算出 24 的方法
最编程
2024-02-01 12:19:37
...
2787:算24
-
总时间限制:
- 3000ms 内存限制:
- 65536kB
-
描述
-
给出4个小于10个正整数,你可以使用加减乘除4种运算以及括号把这4个数连接起来得到一个表达式。现在的问题是,是否存在一种方式使得得到的表达式的结果等于24。
这里加减乘除以及括号的运算结果和运算的优先级跟我们平常的定义一致(这里的除法定义是实数除法)。
比如,对于5,5,5,1,我们知道5 * (5 – 1 / 5) = 24,因此可以得到24。又比如,对于1,1,4,2,我们怎么都不能得到24。
输入
- 输入数据包括多行,每行给出一组测试数据,包括4个小于10个正整数。最后一组测试数据中包括4个0,表示输入的结束,这组数据不用处理。 输出
- 对于每一组测试数据,输出一行,如果可以得到24,输出“YES”;否则,输出“NO”。
-
样例输入
-
5 5 5 1 1 1 4 2 0 0 0 0
样例输出
-
YES NO
这里有点坑,就是算24点的时候那里double和24相等的判断,可能精度有点损失,搞了好久终于AC。
这里可以用穷举的方法来计算,但是为了练习一下递归的思想,就使用了递归来实现。
主要思路如下:
1.将四个数视为一个集合;
2.从集合中随机取两个数,通过加减乘除计算这两个数的结果,然后再将结果放回集合,这样集合就减少了一个元素;
3.重复2的步骤,直到集合中只剩下一个元素为止;
4.查看最后一个元素是否有是24来得出结果。
代码写的有点垃圾,将就记录一下,有时间再改进一下。
在main函数中通过四个循环来对输入进行全排列。
#include <stdio.h>
#include <iostream>
#include <cmath>
#include <string.h>
using namespace std;
double a[4];
int c[4];
int b[4];
char cal_opt[4] = {'+', '-', '*', '/'};
bool flag = false;
bool selected[4];
bool visited[4];
double cal(double a, double b, char opt) {
if (opt == cal_opt[0])
return a + b;
else if (opt == cal_opt[1])
return a - b;
else if (opt == cal_opt[2])
return a * b;
else if (opt == cal_opt[3])
return a / b;
return 0.0;
}
void calculate(int count) {
double result;
if (count == 1) {
for (int i = 0; i < 4; ++i) {
if (!visited[i]) {
result = a[i];
break;
}
}
if (fabs(result - 24.0) < 0.000001) {
cout << "YES" << endl;
flag = true;
}
return;
}
double left, right;
for (int i = 0; i < 3; ++i) {
if (visited[i])
continue;
for (int k = i + 1; k < 4; ++ k) {
if (visited[k])
continue;
left = a[i];
right = a[k];
for (int j = 0; j < 4; ++j) {
if (a[k] == 0 && j == 3) // 除法不能除0
continue;
result = cal(a[i], a[k], cal_opt[j]);
a[i] = result;
visited[k] = 1;
calculate(--count);
if (flag)
goto here;
a[i] = left;
a[k] = right;
visited[k] = 0;
++count;
}
}
}
here:
return;
}
int main() {
while (true) {
bool is_end = true;
for (int i = 0; i < 4; ++i) {
cin >> b[i];
if (b[i] != 0)
is_end = false;
}
if (is_end)
break;
// 生成一个全排列
memset(selected, 0, sizeof selected);
memset(visited, 0, sizeof visited);
flag = false;
for (int i = 0; i < 4; ++i) {
if (selected[i])
continue;
selected[i] = 1;
a[0] = b[i];
for (int j = 0; j < 4; ++j) {
if (selected[j])
continue;
selected[j] = 1;
a[1] = b[j];
for (int k = 0; k < 4; ++k) {
if (selected[k])
continue;
selected[k] = 1;
a[2] = b[k];
for (int l = 0; l < 4; ++l) {
if (selected[l])
continue;
selected[l] = 1;
a[3] = b[l];
calculate(4);
if (flag)
goto here;
selected[l] = 0;
}
selected[k] = 0;
}
selected[j] = 0;
}
selected[i] = 0;
}
here:
if (!flag)
cout << "NO" << endl;
}
return 0;
}
下面这段代码是给出算24点表达式的,包括了加括号过程的恢复,使用了stack和双向队列的数据结构来完成。下面的代码没有经过充分的测试,仅记录一下。
#include <iostream>
#include <stack>
#include <deque>
#include <string>
using namespace std;
double a[4];
double b[4];
char cal_opt[4] = {'+', '-', '*', '/'};
stack<double> mstack;
deque<double> mdeque;
bool flag = false;
bool selected[4];
bool visited[4];
double cal(double a, double b, char opt) {
if (opt == cal_opt[0])
return a + b;
else if (opt == cal_opt[1])
return a - b;
else if (opt == cal_opt[2])
return a * b;
else if (opt == cal_opt[3])
return a / b;
return 0.0;
}
void calculate(int count) {
double result;
if (count == 1) {
for (int i = 0; i < 4; ++i) {
if (!visited[i]) {
result = a[i];
break;
}
}
if (result == 24.0) {
// cout << "24" << endl;
stack<double> result_stack;
stack<string> string_stack;
while (!mdeque.empty()) {
char x[100] = {0};
double left = mdeque.front();
// cout << left << endl;
mdeque.pop_front();
double right = mdeque.front();
// cout << right << endl;
mdeque.pop_front();
char opt = (char) mdeque.front();
// cout << opt << endl;
mdeque.pop_front();
double temp = cal(left, right, opt);
if (!result_stack.empty()) {
if (string_stack.size() == 2){
string r = string_stack.top();
string_stack.pop();
string l = string_stack.top();
string_stack.pop();
r = l + opt + r;
string_stack.push(r);
} else if (left == result_stack.top()) {
sprintf(x, "%c%d", opt, (int)right);
string r(x);
if ((opt == '+' || opt == '-') && !mdeque.empty())
r = "(" + string_stack.top() + r + ")";
else
r = string_stack.top() + r;
string_stack.pop();
string_stack.push(r);
} else if (right == result_stack.top()) {
sprintf(x, "%d%c", (int)left, opt);
string r(x);
if ((opt == '+' || opt == '-') && !mdeque.empty())
r = "(" + r + string_stack.top() + ")";
else
r = r + string_stack.top();
string_stack.pop();
string_stack.push(r);
} else {
result_stack.push(temp);
if (opt == '+' || opt == '-')
sprintf(x, "(%d%c%d)", (int)left, opt, (int)right);
else
sprintf(x, "%d%c%d", (int)left, opt, (int)right);
string r(x);
string_stack.push(r);
}
result_stack.push(temp);
} else {
if (opt == '+' || opt == '-')
sprintf(x, "(%d%c%d)", (int)left, opt, (int)right);
else
sprintf(x, "%d%c%d", (int)left, opt, (int)right);
string r(x);
result_stack.push(temp);
string_stack.push(r);
}
}
flag = true;
cout << string_stack.top() << endl;
}
return;
}
double left, right;
for (int i = 0; i < 3; ++i) {
if (flag)
break;
// if (a[i] == -1.0)
// continue;
if (visited[i])
continue;
for (int k = i + 1; k < 4; ++ k) {
if (flag)
break;
// if (a[k] == -1.0)
// continue;
if (visited[i])
continue;
left = a[i];
right = a[k];
for (int j = 0; j < 4; ++j) {
if (a[k] == 0 && j == 3) // 除法不能除0
continue;
mdeque.push_back(a[i]);
mdeque.push_back(a[k]);
mdeque.push_back(cal_opt[j]);
result = cal(a[i], a[k], cal_opt[j]);
a[i] = result;
visited[k] = 1;
calculate(--count);
if (flag)
break;
mdeque.pop_back();
mdeque.pop_back();
mdeque.pop_back();
visited[k] = 0;
a[i] = left;
a[k] = right;
++count;
}
}
}
}
int main() {
for (int i = 0; i < 4; ++i)
cin >> b[i];
// 生成一个全排列
memset(selected, 0, sizeof selected);
flag = false;
for (int i = 0; i < 4; ++i) {
if (selected[i])
continue;
selected[i] = 1;
a[0] = b[i];
for (int j = 0; j < 4; ++j) {
if (selected[j])
continue;
selected[j] = 1;
a[1] = b[j];
for (int k = 0; k < 4; ++k) {
if (selected[k])
continue;
selected[k] = 1;
a[2] = b[k];
for (int l = 0; l < 4; ++l) {
if (selected[l])
continue;
selected[l] = 1;
a[3] = b[l];
calculate(4);
if (flag)
break;
selected[l] = 0;
}
if (flag)
break;
selected[k] = 0;
}
if (flag)
break;
selected[j] = 0;
}
if (flag)
break;
selected[i] = 0;
}
if (!flag)
cout << "NO" << endl;
calculate(4);
return 0;
}
上一篇: 玩转24点游戏!用C语言轻松实现
下一篇: 玩转经典24点游戏:揭秘算法背后的奥秘