(为方便起见,本题解中用带引号“月”表示某月如1月,带引号“日”表示某日如1日,不带引号日期表示具体日期如1月1日)
这是一道推理题目,要求在一些给定的日期里面找出符合条件的日期。要想解出这道题,首要的不是编程能力,而是推理能力。解出这道题的关键,就是要理解那段奇怪的对话:
A:我不知道她的生日。
B:我知道你不知道她的生日,我也不知道。
A:现在我知道了。
B:我也知道了。
再看给出的样例数据:
7月 1日、3日、8日
8月 2日、3日、6日、7日
9月 1日、8日、10日
10月 4日、5日、6日、9日
11月 5日
想象一下,如果那个日期是11月5日会怎么样?A被告知了“月”,即11月,而选项中满足“月”是11月的,只有11月5日一个日期,那么A将会直接说出“我知道她的生日是11月5日”,而不会说“不知道”。因此,A被告知的“月”肯定不是只出现一次的11月。换句话说,你的第一步就是要排除所有只出现一次的日期。
现在已经排除了11月5日,继续第二步:B说“我知道你不知道她的生日,我也不知道”。这句话是整个程序的关键。B是被告知“日”的,他为什么会仅凭“日”推断出A猜不出来呢?一定是这个“日”对应的所有日期都不能让A仅凭“月”推出来。这样,第一步排除了11月5日,那么第二步就要排除所有5日(因为B只知道5日的情况下不可能排除“11月5日”这个能让A直接推出的选项)。因此,前半句的要求就是排除所有与第一步排除的日期同“日”的日期(通俗的讲,第一步排除了1月1日,那么第二步排除所有1日;第一步排除了11月5日,那么第二步排除所有5日)。后半句类似于第一步,排除只出现一次的2、4、7、9、10日。
让我们看一下现在的情况(加粗表示尚未被排除):
7月 1日、3日、8日
8月 2日、3日、6日、7日
9月 1日、8日、10日
10月 4日、5日、6日、9日
11月 5日
在这时,只知道“月”的A却说他也知道了。那么,符合条件的日期在剩下来的日期里面一定是“月”只出现一次的日期。查表,显然符合条件的只有一个:10月6日。
看似只要分析前三句就可以推理出正确结果,实则远没有那么简单。请看下面的例子:
1月 1日、2日、4日、7日
2月 1日
3月 1日、4日、5日
4月 6日、7日
5月 4日、6日
6月 1日、7日
7月 3日、4日
如果还是按照上面的方法来做,你会得到三个答案:3月4日、6月7日和7月4日。难道这题目有三个解吗?绝不可能。这时候就需要分析最后一句话:B说的“我也知道了”,这意味着类似于第三步A能根据“月”推出来,B也能根据“日”推出来。如果是3.4或7.4,B仅凭“日”是推不出来的。只有6月7日,满足两人都能推出来的要求。
整理一下:
第一步,排除所有“月”只对应一个日期的日期
第二步,排除第一步排除的日期同“日”的日期,以及所有“日”只对应一个日期的日期
第三步,选出未被排除的日期里面“月”只对应一个日期的日期
第四步,在第三步选出的日期里面找出那个“日”只出现一次的日期
那么程序就很简单了。首先,创建一个13*32的列表表示日历(第一行和第一列存储每个“月”和“日”的次数),存储所有的选项日期,代码如下:
c=[[0 for i in range(32)]for j in range(13)]
n=int(input())
for i in range(n):
a,b=map(int,input().split())
c[a][b]+=2
c[0][b]+=1
c[a][0]+=1
第一、二步代码如下:
lst=[]
for q in range(1,13):
if c[q][0]==1:
lst.append(q)
for i in lst:
k=c[i].index(2)
c[i][0]=0
c[i][k]=0
c[0][k]-=1
for j in range(13):
if c[j][k]==2:
c[j][k]=0
c[j][0]-=1
c[0][k]-=1
for i in range(1,32):
if c[0][i]==1:
for j in range(1,13):
if c[j][i]==2:
c[j][i]=0
c[0][i]=0
c[j][0]-=1
第三步代码如下:
for i in range(1,13):
if c[i][0]==1:
k=c[i].index(2)
c[i][k]=3
第四步代码如下:
a=0
b=0
res=[]
for i in range(1,32):
cnt=0
for j in range(1,13):
if c[j][i]==3:
a,b=j,i
cnt+=1
if cnt==1:
print(a,b)
exit(0)