使用程序解决一道逻辑推理题
? ? ? ? 今天看朋友發(fā)了一個(gè)老問題,一道很有意思的推理題:(轉(zhuǎn)載請(qǐng)指明出于breaksoftware的csdn博客)
? ? ? ? 小明和小強(qiáng)都是張老師的學(xué)生,張老師的生日是M月N日,2人都知道張老師的生日是下列10組中的一天:
? ? ? ? 3月4日 3月5日 3月8日
? ? ? ??6月4日 6月7日
? ? ? ??9月1日 9月5日
? ? ? ??12月1日 12月2日 12月8日
? ? ? ? 張老師將M值告訴了小明,將N值告訴了小強(qiáng),張老師問他們知道他的生日是哪一天嗎?
? ? ? ? 小明說(shuō):如果我不知道的話,小強(qiáng)肯定不知道
? ? ? ? 小強(qiáng)說(shuō):本來(lái)我也不知道,但是現(xiàn)在我知道了
? ? ? ? 小明說(shuō):哦,那我也知道了
? ? ? ? 據(jù)上面信息,推斷出張老師的生日是哪一天?
? ? ? ? 這個(gè)邏輯題,如何用程序?qū)崿F(xiàn)?其實(shí)這是一個(gè)建模過程,我們需要用專業(yè)的術(shù)語(yǔ)重新描述這個(gè)邏輯。
? ? ? ? 這個(gè)問題數(shù)據(jù)只有2個(gè):月數(shù)和天數(shù)。邏輯是參雜2個(gè)人角度看問題的3句話。我們分析這個(gè)問題時(shí),首先要保持第三者的視角,逐個(gè)從其他兩個(gè)視角去分析這個(gè)問題。然后就是建立模型,我們看這樣的數(shù)據(jù)有個(gè)特征:{Key,Value}鍵值對(duì)。但是可以看出這是個(gè)MultiMap,即一個(gè)鍵可以對(duì)應(yīng)多個(gè)值。
? ? ? ? 我們沿著這個(gè)思路走.可以發(fā)現(xiàn),站在小明的角度,我們可以將數(shù)據(jù)建立成一個(gè)MultiMap。他眼中的數(shù)據(jù)使用月數(shù)M為鍵,天數(shù)N為值。以后我們稱下表為“小明表”。
? ? ? ? 可以站在小強(qiáng)的角度,我們將數(shù)據(jù)建立成一個(gè)新的MultiMap。他眼中的數(shù)據(jù)使用天數(shù)N為鍵,月數(shù)M為值。以后我們稱下標(biāo)為“小強(qiáng)表”。
? ? ? ? 我們?cè)倩氐降谌叩慕嵌?#xff0c;可以得出,這兩張表對(duì)于小明和小強(qiáng)都是可見的。
? ? ? ? 我們將小明和小強(qiáng)的對(duì)話,一條一條轉(zhuǎn)換為約束條件。
? ? ? ? 1?小明說(shuō):如果我不知道的話,小強(qiáng)肯定不知道
? ? ? ? 小明是看了“小強(qiáng)表”之后得出以上結(jié)論。這句話意味著:他所知的M值在“小強(qiáng)表”中不存在Key Value唯一對(duì)應(yīng)關(guān)系。即12月2日和6月7日,這兩個(gè)月份12和6都不是老師的生日月數(shù)。因?yàn)槿绻荕是12或6,小明在不知道N的情況下,無(wú)法給定如此“拽”的回答。于是逐步排除出一下結(jié)果(紅色代表排除的選項(xiàng))
? ? ? ? 2?小強(qiáng)說(shuō):本來(lái)我也不知道,但是現(xiàn)在我知道了
? ? ? ? 小強(qiáng)在看到上圖后,得出上面結(jié)論。這個(gè)說(shuō)明,小強(qiáng)知道的N在上表中是Key Value唯一對(duì)應(yīng)關(guān)系。于是得出
? ? ? ? 因?yàn)樾?qiáng)知道N是多少,所以剩下的選項(xiàng)中,他知道正確答案了。只是我們還不知道。我們期待小明的話。
? ? ? ? 3?小明說(shuō):哦,那我也知道了
? ? ? ? 對(duì)于小明,他和我們一樣,可以看到上圖。于是他知道N的值只可能是1、4、8。于是修改“小明表”為
? ? ? ? 由于此時(shí)小明已經(jīng)知道了答案。可以見得M值在上表中是Key Value唯一對(duì)應(yīng)關(guān)系。于是我們可以排除3和12。得出
? ? ? ? 此時(shí)有兩個(gè)答案。我們此時(shí)結(jié)合篩選后的“小強(qiáng)表”
? ? ? ? 此時(shí),我們可以說(shuō)6月4日在“小強(qiáng)表”中已被排除,所以我們選擇9月1日。或者我們從這個(gè)兩個(gè)表中找到了唯一的共同選項(xiàng),從而得知是9月1日。
? ? ? ? 草編了一下代碼
// ACM.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
//#include "stdafx.h"
#include <list>
/*
小明和小強(qiáng)都是張老師的學(xué)生,張老師的生日是M月N日,
2人都知道張老師的生日是下列10組中的一天,
張老師將M值告訴了小明,將N值告訴了小強(qiáng),
張老師問他們知道他的生日是哪一天嗎?
3月4日 3月5日 3月8日
6月4日 6月7日
9月1日 9月5日
12月1日 12月2日 12月8日
小明說(shuō):如果我不知道的話,小強(qiáng)肯定不知道
小強(qiáng)說(shuō):本來(lái)我也不知道,但是現(xiàn)在我知道了
小明說(shuō):哦,那我也知道了
據(jù)上面信息,推斷出張老師的生日是哪一天?
*/
struct stBirthday{int nMonth;int nDay;bool bMaybe;bool bMaybeSecond;bool bMaybeThird;
};static stBirthday g_BirthdayArray[] = {{3,4,false,false,true},{3,5,false,false,true},{3,8,false,false,true},{6,4,false,false,true},{6,7,false,false,true},{9,1,false,false,true},{9,5,false,false,true},{12,1,false,false,true},{12,2,false,false,true},{12,8,false,false,true},
};int g_nArrayCount = sizeof(g_BirthdayArray)/sizeof(stBirthday);typedef std::list<stBirthday> ListBirthday;
typedef ListBirthday::iterator ListBirthdayIter;void XiaoMingFirst(ListBirthday& listBirthday)
{// 小明知道月數(shù)后,說(shuō):如果我不知道,小強(qiáng)肯定也不知道// 這意味著小明看了他所知道月數(shù)里的每個(gè)天數(shù)在其他月數(shù)里都能找到// 于是天數(shù)具有唯一性的選項(xiàng)是“不可能”的for ( ListBirthdayIter it = listBirthday.begin(); it != listBirthday.end(); it++ ) {if ( it->bMaybe ) {// 該值可能在之后的邏輯中提前被設(shè)置,所以不用比較// 這個(gè)日期是存在可能的continue;}for ( ListBirthdayIter iter = it; iter != listBirthday.end(); iter++){if ( iter == it ) {// 不和自身比較continue;}if ( it->nDay == iter->nDay ) {// 第一個(gè)答案的提煉的思想就是:// 小明看了他所知道月數(shù)里的每個(gè)天數(shù)在其他月數(shù)里都能找到// 所以,沒有小明的答案,小強(qiáng)肯定不知道確切的幾月幾日// 于是,我們將有天數(shù)有重復(fù)的答案認(rèn)定為可能的選項(xiàng)it->bMaybe = iter->bMaybe = true;}}}for ( ListBirthdayIter it = listBirthday.begin(); it != listBirthday.end(); it++){if ( it->bMaybe ) {continue;}// 經(jīng)過上輪處理,需要將該月里可能同時(shí)存在“可能”和“不可能”的選項(xiàng)的可能性都設(shè)置為“不可能”// 因?yàn)樾∶骺戳怂涝聰?shù)里的每個(gè)日數(shù)在其他月數(shù)里“都”能找到,我們要配合“都”這個(gè)必要條件for ( ListBirthdayIter iter = listBirthday.begin(); iter != listBirthday.end(); iter++){if ( it->nMonth == iter->nMonth ) {iter->bMaybe = false;}}}
}void XiaoQiangFirst(ListBirthday& listBirthday)
{// 小強(qiáng)分析了小明回答后,回答:本來(lái)我也不知道,但是現(xiàn)在我知道了// 這意味著小明的答案給小強(qiáng)提供了月數(shù)信息// 因?yàn)樾∶鞯幕卮鹱屗诖x擇的多個(gè)結(jié)果中排除了其他可能性,只有一個(gè)選擇// 于是編碼的思路就是:// 1 在已經(jīng)“不可能”的月數(shù)中,尋找其對(duì)應(yīng)的天數(shù)在“可能”的月中是否有對(duì)應(yīng)關(guān)系// 或者// 2 在已經(jīng)“可能”的月數(shù)中,尋找其對(duì)應(yīng)的天數(shù)在“不可能”的月中是否有對(duì)應(yīng)關(guān)系// 以下編碼選擇1思路實(shí)現(xiàn)for ( ListBirthdayIter it = listBirthday.begin(); it != listBirthday.end(); it++ ) {if ( it->bMaybe ) {// 尋找“不可能”的月數(shù),于是“可能”的月數(shù)不作為可選條件continue;}for ( ListBirthdayIter iter = listBirthday.begin(); iter != listBirthday.end(); iter++){if ( false == iter->bMaybe ){// 在找到一個(gè)“不可能”的月數(shù)情況下,尋找“可能”的月數(shù),以作下步篩選continue;}if ( it->nDay == iter->nDay ) {// 存在對(duì)應(yīng)關(guān)系,則該“可能”日期經(jīng)過第二輪篩選,還是“可能”的iter->bMaybeSecond = true;}}}
}void XiaoMingSecond(ListBirthday& listBirthday)
{// 小明在聽到小強(qiáng)的回答后,說(shuō):哦,那我也知道了。// 這意味著小強(qiáng)的答案已經(jīng)為小明提供了天數(shù)信息// 在可能眾多的選項(xiàng)中,小明卻知道了答案,// 證明信息經(jīng)過小強(qiáng)篩選過后,小明所知道的月數(shù)中,只有一個(gè)天數(shù)答案for ( ListBirthdayIter it = listBirthday.begin(); it != listBirthday.end(); it++ ) {if ( false == it->bMaybeSecond ) {it->bMaybeThird = false;continue;}for ( ListBirthdayIter iter = it; iter != listBirthday.end(); iter++){if ( iter == it ) {// 不和自身比較continue;}if ( false == iter->bMaybeSecond || false == iter->bMaybeThird ) {// 不滿足條件的不做比較continue;}if ( it->nMonth == iter->nMonth ) {// 經(jīng)過兩輪篩選,如果有兩個(gè)選項(xiàng)是同一個(gè)月數(shù)的// 則可以認(rèn)為該數(shù)月的所有選項(xiàng)都是“不可能”for ( ListBirthdayIter iterIn = it; iterIn != listBirthday.end(); iterIn++){if ( it->nMonth == iterIn->nMonth ) {iterIn->bMaybeThird = false;}}}}}
}void TestBirthday()
{ListBirthday listBirthday;for ( int n = 0; n < g_nArrayCount; n++ ) {listBirthday.push_back(g_BirthdayArray[n]);}XiaoMingFirst(listBirthday);printf("The First Result:\n");for ( ListBirthdayIter it = listBirthday.begin(); it != listBirthday.end(); it++ ) {if ( it->bMaybe ){printf("Month:%d Day:%d\n", it->nMonth, it->nDay);} }XiaoQiangFirst(listBirthday);printf("The Second Result:\n");for ( ListBirthdayIter it = listBirthday.begin(); it != listBirthday.end(); it++ ) {if ( it->bMaybeSecond ){printf("Month:%d Day:%d\n", it->nMonth, it->nDay);} }XiaoMingSecond(listBirthday);printf("The Third Result:\n");for ( ListBirthdayIter it = listBirthday.begin(); it != listBirthday.end(); it++ ) {if ( it->bMaybeThird ){printf("Month:%d Day:%d\n", it->nMonth, it->nDay);} }
}int _tmain(int argc, _TCHAR* argv[])
{TestBirthday();return 0;
}
總結(jié)
以上是生活随笔為你收集整理的使用程序解决一道逻辑推理题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 以金山界面库(openkui)为例思考和
- 下一篇: WMI技术介绍和应用——查询硬件信息