1. 问题描述:
农夫约翰和奶牛贝茜喜欢在业余时间互相出数学题。约翰给贝茜出了一道相当难的问题,导致她没能解决。现在,她希望通过给约翰出一道有挑战性的难题来报复他。贝茜给了约翰一个表达式 (B+E+S+S+I+E)(G+O+E+S)(M+O+O),其中包含七个变量 B,E,S,I,G,O,M(O 是变量,不是零)。对于每个变量,她给约翰一个列表,表中包含该变量可采用的最多 20 个整数值。她要求约翰计算,共有多少种给变量赋值的方法可以使得表达式的计算结果为偶数。
输入格式
第一行包含一个整数 N。接下来 N 行,每行包含一个变量和该变量的一个可能值。每个变量至少出现 1 次,最多出现 20 次。同一变量不会重复列出同一可能值。
输出格式
输出可以使得表达式的计算结果是偶数的给变量赋值的方法总数。
数据范围
7 ≤ N ≤ 140,
所有变量的可能取值范围 [−300,300],本题答案不会超出int范围。
输入样例:
10
B 2
E 5
S 7
I 10
O 16
M 19
B 3
G 1
I 9
M 2
输出样例:
6
样例解释
共有 6 种可能的赋值方式:
(B,E,S,I,G,O,M) = (2, 5, 7, 10, 1, 16, 19) -> 53,244
= (2, 5, 7, 10, 1, 16, 2 ) -> 35,496
= (2, 5, 7, 9, 1, 16, 2 ) -> 34,510
= (3, 5, 7, 10, 1, 16, 2 ) -> 36,482
= (3, 5, 7, 9, 1, 16, 19) -> 53,244
= (3, 5, 7, 9, 1, 16, 2 ) -> 35,496
注意,(2, 5, 7, 10, 1, 16, 19) 和 (3, 5, 7, 9, 1, 16, 19),虽然计算结果相同,但是赋值方式不同,所以要分别计数。
来源:https://www.acwing.com/problem/content/1877/
2. 思路分析:
首先我们需要想一下如何将题目先做出来再考虑如何优化,由题目可知每一种字母最多有20种取值,所以在最坏情况下的方案数目为20 ^ 7 = 128000000那么一定会超时的所以我们考虑如何对其进行优化,可以发现最终我们关注的是等式的奇偶性而不是等式最终的结果,题目中要求判断出对于某种选择方案最终的结果是奇数还是偶数等价于表达式的结果对于2取余的结果是多少,而取余的等式中是存在某些性质的,例如(a + b) % c = (a % c + b % c) % c也即可以分开求解,基于这个想法我们考虑所有的字母对于2取余的结果是奇数还是偶数的情况进行枚举,也即对于2取余的结果为0还是为1,所以只需要枚举字符串"BESIGOM"中每一个字母对于2取余的结果是奇数还是偶数的所有状态即可,也即枚举2 ^ 7种状态即可(枚举对应的十进制数字即可),并且在一开始的时候我们可以使用两个哈希表mp1和mp2分别记录出现奇数还是偶数次数的字母出现的次数,并且使用一个哈希表mp来记录当前枚举的状态中各个字母对于2的取余情况方便判断当前的等式结果对于2取余的结果是否是偶数,当满足条件之后那么累加对应的方案数目即可。并且表达式:(B+E+S+S+I+E)(G+O+E+S)(M+O+O)对于2取余的结果可以先化简一下(如果式子中出现了偶数次的字母那么对于2取余的结果为0所以对于结果没有什么影响直接忽略掉),为:(B + I) * (G + O + E + S) * M,对于某个状态是0还是1的一般可以使用dfs(dfs递归两个平行状态)或者是二进制枚举来解决。
3. 代码如下:
dfs:
import collections
class Solution:
res = None
# 模拟每一个字母的两种选择: 对于2的余数是0还是1, mp是一个存储中间结果的哈希表这样在递归出口可以判断等式是否满足条件, x记录方案数目
def dfs(self, u: int, x: int, s: str, mp: dict, mp1: collections.defaultdict, mp2: collections.defaultdict):
# 递归出口
if u == 7:
# 判断当前每一种字母对应的奇数还是偶数的选择下最终等式对2的取余的结果是否是0
if (mp["B"] + mp["I"]) * (mp["G"] + mp["O"] + mp["E"] + mp["S"]) * mp["M"] % 2 == 0:
# 满足等式的要求
self.res += x
return
c = s[u]
# 尝试当前的字符对于2取余的结果为奇数的情况
mp[c] = 1
self.dfs(u + 1, x * mp1[c], s, mp, mp1, mp2)
# 尝试当前的字符对于2取余的结果为偶数的情况
mp[c] = 2
self.dfs(u + 1, x * mp2[c], s, mp, mp1, mp2)
def process(self):
n = int(input())
# mp1, mp2分别记录出现奇数次和偶数次的字母出现的次数是多少
mp1, mp2 = collections.defaultdict(int), collections.defaultdict(int)
for i in range(n):
s = input().split()
if int(s[1]) % 2 == 0:
# 偶数
mp2[s[0]] += 1
else:
# 奇数
mp1[s[0]] += 1
# 题目中出现的7个字母
s = "BESIGOM"
# 初始化全局变量
self.res = 0
self.dfs(0, 1, s, dict(), mp1, mp2)
return self.res
if __name__ == "__main__":
print(Solution().process())
二进制:
import collections
class Solution:
def process(self):
n = int(input())
# mp1, mp2分别用来统计出现奇数次数和偶数次数的字母出现的次数
mp1, mp2 = collections.defaultdict(int), collections.defaultdict(int)
for i in range(n):
s = input().split()
if int(s[1]) % 2:
mp1[s[0]] += 1
else:
mp2[s[0]] += 1
mp = dict()
# 对应题目中的7个字母
s = "BESIGOM"
res = 0
for i in range(1 << 7):
for j in range(7):
# 求解当前状态i对应的第j位是否是1
mp[s[j]] = i >> j & 1
# 判断等式是否满足要求
if (mp["B"] + mp["I"]) * (mp["G"] + mp["O"] + mp["E"] + mp["S"]) * mp["M"] % 2 == 0:
# 求解方案数目
_sum = 1
for j in range(7):
if i >> j & 1:
_sum *= mp1[s[j]]
else:
_sum *= mp2[s[j]]
res += _sum
return res
if __name__ == "__main__":
print(Solution().process())