1 题目描述
2 解题思路
2.1 遍历所有可能情况
我们先找出四张牌所有可能的排列组合。
然后对于每一种排列组合,我们先提出排列组合中最前面的两个数。然后对这两个数进行加\减\乘\除操作。将结果放入我们当前考虑的排列组合中。
此时排列组合中有三个元素:原来的后两个元素。和前两个元素进行数学计算后的结果。
慢的操作是对这三个数组成的序列,再求它们 的排列组合。但实际上后两个数的顺序我们是可以固定的。也就是说,如果我们接下来提取出来的两个数。有前两个数运算后的结果,那么这个结果可能在运算符的左边,也可能在运算符的右边。这两种情况我们都需要考虑
然后三个数组成的序列,经过上面一部变成了两个数。同第三步的考虑一样。两个操作数哪个在运算符的左边,哪个在右边。我们都需要进行讨论和考虑。
还有一个tips。因为这种方法我们没有排除掉除以0 的情况。为了防止除0报错,进行除法的时候,我们需要对被除数加上一个很小的数值。
class Solution:
def judgePoint24(self, cards: List[int]) -> bool:
ret=[]
for i in cards:
tmp=copy.deepcopy(cards)
tmp.remove(i)
for j in tmp:
tmp1=copy.deepcopy(tmp)
tmp1.remove(j)
for k in tmp1:
tmp2=copy.deepcopy(tmp1)
tmp2.remove(k)
l=tmp2[0]
ret.append([i,j,k,l])
#四张牌所有的排列组合
'''
求排列组合的大致思想是:
我们先从四个元素组成的序列中提取一个;
然后从不含这个元素的三元素序列中提取一个;
然后是两个中选一个,最后就是另外的一个
'''
def judge2(ab,cd):
#print(ab,cd)
return(
(abs(ab+cd-24)<0.001)
or
(abs(ab*cd-24)<0.001)
or
(abs(ab-cd-24)<0.001)
or
(abs(ab/(cd+0.0000001)-24)<0.001)
or
(abs(cd-ab-24)<0.001)
or
(abs(cd/(ab+0.0000001)-24)<0.001)
)
#三元序列中提取两个进行计算,得到的结果和序列中剩余的一个,这两个数能否通过某种运算达到24
def judge3(ab,c,d):
#print(ab,c,d)
return(
judge2(ab+c,d)
or
judge2(ab-c,d)
or
judge2(ab*c,d)
or
judge2(ab/(c+0.0000001),d)
or
judge2(c/(ab+0.0000001),d)
or
judge2(c-ab,d)
or
judge2(ab,c+d)
or
judge2(ab,c-d)
or
judge2(ab,c*d)
or
judge2(ab,c/(d+0.0000001)))
#四元序列中提取两个进行运算,得到的结果和序列中剩余的两个,这三个数能否通过什么方法使结果到24
def judge4(a,b,c,d):
return(
judge3(a+b,c,d)
or
judge3(a-b,c,d)
or
judge3(a*b,c,d)
or
judge3(a/(b+0.0000001),c,d))
#四个数能否通过某些操作使得结果为24
for i in ret:
if(judge4(i[0],i[1],i[2],i[3])==True):
return(True)
return(False)
2.2 方法2 :递归
class Solution:
def judgePoint24(self, nums: List[int]) -> bool:
TARGET = 24
EPSILON = 1e-6
ADD, MULTIPLY, SUBTRACT, DIVIDE = 0, 1, 2, 3
def solve(nums: List[float]) -> bool:
if len(nums) == 1:
return abs(nums[0] - TARGET) < EPSILON
#只剩下一个数了,就判断这个数和24之间的差距是否在临界差距以内
#在的话,那么说明可以通过运算得到24
for i, x in enumerate(nums):
for j, y in enumerate(nums):
if i != j:
#从nums中找不同的两个数(区别于方法2.1的思路)
newNums = list()
for s, z in enumerate(nums):
if s != i and s != j:
newNums.append(z)
#找第三个数
for k in range(4):
if k < 2 and i > j:
continue
'''
加和乘运算,而且第一个运算数比第二个大
这说明之前在x等于第二个运算数的时候,x+y 或者x*y已经计算过了
所以不用再次计算,直接继续循环
'''
if k == ADD:
newNums.append(x + y)
elif k == MULTIPLY:
newNums.append(x * y)
elif k == SUBTRACT:
newNums.append(x - y)
elif k == DIVIDE:
if abs(y) < EPSILON:
continue
newNums.append(x / y)
if solve(newNums):
return True
#如果x,y运算的结果和nums中剩下的两个数组成的三个数可以算出24,那么返回True
newNums.pop()
'''
不然的话,在三元素序列中,把当前算的这个达不到24的x,y运算结果pop出来,
然后看下一个操作符算出来的X,Y结果,和nums中剩下的两个元素组成的三元素序列满足不满足条件要求
'''
return False
return solve(nums)
2.3 方法3:dfs
class Solution:
def judgePoint24(self, cards: List[int]) -> bool:
def dfs(nums):
if len(nums) == 1:
return abs(nums[0] - 24) < 0.00001
#序列中只有最后一个数了,进行判断
for i in range(len(nums)-1):
for j in range(i+1, len(nums)):
x, y = nums[i], nums[j]
#提取两个不一样的数(这里假设x在序列中的排位一定在y前面)
#所以之后考虑减法和除法的时候,需要分别考虑x和y在除/减数和被除/减数的情况
rst = nums[:i] + nums[i+1:j] + nums[j+1:]
#x和y元素以外的元素拼成一个序列
a = dfs(rst + [x + y])
b = dfs(rst + [x - y])
c = dfs(rst + [y - x])
d = dfs(rst + [x * y])
e = (y != 0) and dfs(rst + [x / y])
f = (x != 0) and dfs(rst + [y / x])
#将x,y经过不同操作数的结果放到rst中,拼接而成的序列继续进行dfs
if a or b or c or d or e or f:
return True
return False
return dfs(cards)