HDU 1427 速算24点 (深搜)

时间:2021-11-02 07:10:11

题目链接

Problem Description

速算24点相信绝大多数人都玩过。就是随机给你四张牌,包括A(1),2,3,4,5,6,7,8,9,10,J(11),Q(12),K(13)。要求只用'+','-','*','/'运算符以及括号改变运算顺序,使得最终运算结果为24(每个数必须且仅能用一次)。游戏很简单,但遇到无解的情况往往让人很郁闷。你的任务就是针对每一组随机产生的四张牌,判断是否有解。我们另外规定,整个计算过程中都不能出现小数。

Input

每组输入数据占一行,给定四张牌。

Output

每一组输入数据对应一行输出。如果有解则输出"Yes",无解则输出"No"。

Sample Input

A 2 3 6

3 3 8 8

Sample Output

Yes

No

题目描述:

给定4个数字,判定这4个数字是否可以通过运算得到结果24。运算符包括:加、减、乘、除,允许变换数字的顺序,当然也可以使用括号改变运算顺序。

即:判定是否存在一种通过在下面的圆圈中添加运算符以及添加括号的方式使得等式成立:

a ○ b ○ c ○ d = 24

例如:数字 5, 5, 5, 1 可以通过运算得到结果24:

5 × (5 - 1 ÷ 5) = 24

数字 9, 9, 9, 9 则无论通过怎样的运算都无法得到24的结果。

分析:

最开始的时候想的情况比较复杂,后来发现其实对于使用小括号来改变运算顺序的话,完全可以通过改变数字的运算顺序来达到相同的目的,考虑的情况就会大大减少,因此可以采用暴力搜索的方式进行解答,枚举所有的可能的运算方式,如果有1种方案可以达到24,则表明给定的数据有解,否则无解。

枚举的方式如下:

由于加法和乘法满足交换律,而减法和除法则不满足,这里我们添加两种运算:“反减”和“反除”,分别记作 ~- 和 ~÷,此时:

a ~- b = b - a

a ~÷ b = b ÷ a

添加这两种运算操作后,就使得减法和除法也同样符合交换律。此时,所有情况的运算顺序就只有两种情况,用※表示任意的运算符,则两种运算顺序为:

((a ※ b) ※ c) ※ d)) 从左到右依次计算

((a ※ b) ※ (c ※ d)) 分别计算前两个和后两个数字的结果后,在将得到的两个结果进行运算,得到最终的结果

而其他的运算顺序均可以通过调整数字的排列顺序得到用以上两种情况表示的等价情况,例如:

(a ※ ((b ※ c) ※ d)) =====> (((b ※ c) ※ d) ※ a)

((a ※ (b ※ c)) ※ d) =====> (((b ※ c) ※ a) ※ d)

这样,通过枚举4个数字排列情况和三个位置的运算符的不同情况,就可以枚举到所有的运算情况。其中:4个数字排列,共有4!= 24种结果,而3个运算符中每个运算符都有6种不同的情况,因此共有6^3 = 216中情况,而运算顺序又有2中情况,因此,总的情况数为:

24 × 216 × 2 = 10368

由于加法和乘法本身就具有交换律,并且给定的4个数字也有某些数字相同的情况,因此,在实际枚举过程中,10368种情况会有某些是重复的。本题数据量比较小,因此重复计算的问题可以忽略。

代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<string.h>
using namespace std;
int num[4];
bool flag;
void dfs(int sum,int now,int times)
{
if(!flag)
return ;
if(times==3)
{
if(sum+now==24||sum-now==24||sum*now==24)
flag=false;
if(now!=0&&sum%now==0&&sum/now==24)
flag=false;
return;
}
//先计算前面的一部分
dfs(sum+now,num[times+1],times+1);
dfs(sum-now,num[times+1],times+1);
dfs(sum*now,num[times+1],times+1);
if(now!=0&&sum%now==0)
dfs(sum/now,num[times+1],times+1);
//再计算后面一部分
dfs(sum,now+num[times+1],times+1);
dfs(sum,now-num[times+1],times+1);
dfs(sum,now*num[times+1],times+1);
if(num[times+1]!=0&&now%num[times+1]==0)
dfs(sum,now/num[times+1],times+1);
}
int main()
{
char ch[6];
while(~scanf("%s",ch))
{
flag=true;
if(strlen(ch)==2)//除了数字10之外,所有的数字都是一位字符来表示的e
num[0]=10;
else
{
if(ch[0]=='A')
num[0]=1;
else if(ch[0]=='J')
num[0]=11;
else if(ch[0]=='Q')
num[0]=12;
else if(ch[0]=='K')
num[0]=13;
else num[0]=ch[0]-'0';
}
for(int i=1; i<=3; i++)
{
scanf("%s",ch);
if(strlen(ch)==2)//除了数字10之外,所有的数字都是一位字符来表示的e
num[i]=10;
else
{
if(ch[0]=='A')
num[i]=1;
else if(ch[0]=='J')
num[i]=11;
else if(ch[0]=='Q')
num[i]=12;
else if(ch[0]=='K')
num[i]=13;
else num[i]=ch[0]-'0';
}
}
sort(num,num+4);
do
{
dfs(num[0],num[1],1);
}
while(next_permutation(num,num+4)&&flag);//全排列函数来枚举四个数字的任意的顺序
if(!flag)//只要有一种情况计算出来结果
printf("Yes\n");
else
printf("No\n");
}
return 0;
}