枚举法用于逻辑问题的处理

前言

遇到一些逻辑问题的时候,因为数据量不大的关系,我们通常只是需要人工枚举出所有的情况就可以。今天发现了怎么用计算机去枚举,况且记录一波。

逻辑枚举

问题 1:
警察局抓了a,b,c,d四名偷窃嫌疑犯,其中有一人是小偷。审问中:
a说:“我不是小偷”。
b说:“c是小偷”。
c说:“小偷肯定是d”。
d说:“c冤枉人”。
现在已经知道四人中三人说的是真话,一人说的是假话。问到底谁是小偷?

分析
问题只有4中情况,而且他们4个人说的话只有4句为真。假设小偷为x,他们四个人说的话可以转换为以下四条逻辑表达式:

  • a的话:x !=a
  • b的话:x = c
  • c的话:x = d
  • d的话:x != d

接下来就只需要枚举x可取的四个值,满足上面三条逻辑表达式有三条为真的情况就得到了正解

代码如下

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
	int x;
	for (x = 'a'; x <= 'd'; x++)
		if (((x != 'a') + (x == 'c') + (x == 'd') + (x !=' d')) == 3)
			printf("%c",x);
	return 0;
} 

问题 2:
3位老师对某次数学竞赛进行预测,他们的预测为:
甲说: 学生A得第一名,学生B得第三名
乙说:学生C得第一名,学生D得第四名
丙说:学生D得第二名,学生A得第三名。
竞赛结果表明,他们都说对一半,说错一半,且无并列名次,试编写程序a,b,c,d各自的名称。

分析
如果不除去并列名次,则有 4 4 4^4 44种情况,人工枚举会稍显麻烦。我们还是可以按照上一题的思想进行枚举。
假设a,b,c,d分别表示学生A,学生B,学生C和学生D所获得的名次,那么三位教练说的话可以可以用如下逻辑表达式表示:

  • 甲说的话:a = 1 , b = 3
  • 乙说的话:c = 1 , d = 4
  • 丙说的话:d = 2 , a = 3

由于他们说的话都只有一半是对的,所以我们可以得到以下条件句:

  • (a=1) + (b=3) = 1
  • (c=1) + (d=4) = 1
  • (d=2) + (a=3) = 1

代码如下

#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
	int a,b,c,d;
	for(a=1;a<=4;a++){
		for(b=1;b<=4;b++)if(a!=b){//去除重复名次
			for(c=1;c<=4;c++)if(a!=c&&b!=c){
				d=10-a-b-c;  //根据a,b,c的名次计算d的名次
				if(a!=d&&b!=d&&c!=d){
					if((a==1)+(b==3)==1&&(c==1)+(d==4)==1&&(d==2)+(a==3)==1)
						printf("a=%d, b=%d, c=%d, d=%d",a,b,c,d);
				}
			}
		}
	}
	return 0;
} 

实战演练

问题描述
  公安人员审问甲、乙、丙、丁四个嫌疑犯,已确知,这四个人当中仅有一人是偷窃者,还知道这四个人的答话,要么完全诚实,要么完全说谎。在回答公安人员的问话中:
  甲说:“乙没有偷,是丁偷的。”
  乙说:“我没有偷,是丙偷的。”
  丙说:“甲没有偷,是乙偷的。”
  丁说:“我没有偷,我用的那东西是我家里的。”
  请根据上述四人答话,判断谁是偷窃者。
输入格式

输出格式
输出一个字符,表示偷窃者是谁,A表示甲,B表示乙,C表示丙,D表示丁。

分析

  • 这对绝大多数人来说,都只是一道水题。虽然情况不少,我们只要通过人工的逻辑推理很快可以得到正确的答案。
  • 下面我们继续用上面讲解到的思路进行求解,我们假设凶手为x,可以得到他们说的话对应的逻辑表示式:
    • 甲说的话表示x != B , x = D
    • 乙说的话表示x != B , x = C
    • 丙说的话表示x != A , x = B
    • 丁说的话表示x != D
  • 因为甲乙丙说的话都包含两个部分,而且他们说的话要么全为真,要么全为假。(至于丁说的话对结果有没有影响,我还有点疑问)就可得以下条件句:
    • (x!=‘B’)+(x=‘D’)!=1
    • (x!=‘B’)+(x=‘C’)!=1
    • (x!=‘A’)+(x=‘B’)!=1

代码如下

#include <iostream>
#include <cstdio>

#define x1 (x!='B')+(x=='D')
#define x2 (x!='B')+(x=='C')
#define x3 (x!='A')+(x=='B')

using namespace std;

int main()
{
	int x;
	for(x='A';x<='D';x++)
		if((x1!=1)&&(x2!=1)&&(x3!=1))
			printf("%c",x);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值