思维的过程才是最重要的,我们要的不止是金蛋,更是那只下金蛋的鸡。
———此类想法总结句
传说爱因斯坦出过这样一道逻辑题,此题有18个条件,要求大家最后推理出问题的答案。是否真为爱因斯坦所出我也无法考证,当然我想这只是出题人为了引起大家的注意罢了,但是题目本身还是挺有意思的,也属于实际生活中的问题了(别较真...)。我们先说说这道题本身:
1、在一条街上,有5座房子,喷了5种颜色。 2、每个房子里住着不同国籍的人。 3、每个人喝着不同的饮料,抽不同品牌的香烟,养不同的宠物。 4、英国人住红色房子。5、瑞典人养狗。 6、丹麦人喝茶。 7、绿色房子在白色房子左面。 8、绿色房子主人喝咖啡。9、抽Pall Mall香烟的人养鸟。 10、黄色房子主人抽Dunhill香烟。 11、住在中间房子的人喝牛奶。 12、挪威人住第一间房。 13、抽Blends香烟的人住在养猫的人隔壁。 14、养马的人住抽Dunhill香烟的人隔壁。 15、抽Blue Master的人喝啤酒。 16、德国人抽Prince香烟。 17、挪威人住蓝色房子隔壁。 18、抽Blends香烟的人有一个喝水的邻居。 问:哪个国家的人养鱼?
初见此题,若纯粹使用逻辑推理的方法是肯定做得出来的,但是若用计算机来求解此类实际问题,又应当如何去做呢?
首先,面对这18个条件,我们既然要让计算机去做,就首先得把看似复杂的问题简单化(因为:一切复杂的东西都是由很多简单的事务所组成的——某名人名言),在计算机的领域里,就是我们所说的复杂度的控制问题。这里的复杂度有两个方面:第一、题目本身的复杂度;第二、针对题目编程的实现复杂度(程序是否容易实现、是否易懂、程序本身是否易读等等都需要我们考虑)。那么针对这个题目来说,条件这么多,我们如何下手呢?先别急,让我们现将题目大致浏览一下,找到里面所涉及的元素有哪些?通过阅读我们可以发现,这里涉及的元素有如下几个:房子(House)、房子颜色(HouseColor)、国籍(Nationality)、饮料(Beverage)、香烟(Cigarette)、宠物(Pets),找到元素以后,我们接下来去看一下元素之间的关系,找到其中自己感觉比较棘手的要点。通过对题目的大致浏览,我们不难发现,这些条件中最麻烦的部分,就是关于:隔壁、邻居、左面这些位置信息的表达。至于说类似:英国人住红色房子等等这一类条件,倒是很好转化为条件表达式。对于位置信息的表达,我们可以挖掘出一个隐含元素,那个就是门牌号(House number),通过对这一隐含元素的挖掘,我们就能够很好地处理好关于房子位置的信息了。这个时候我们可以初步做一些编程的“规划"了,这里为了提高程序的可读性(也是控制程序复杂度的一个方面!),我们使用枚举来进行对于不同种类的事务进行标识,具体如下:
enum PET //宠物
{
Dog = 0,
Fish,
Horse,
Cat,
Bird
};
enum HOUSECOLOR //房子颜色
{
Red = 5,
Green,
Yellow,
Blue,
White
};
enum BEVERAGE //饮料
{
Milk = 10,
Coffee,
Tea,
Beer,
Water
};
enum NATIONALITY //国籍
{
Germany = 15,
Norway,
British,
Sweden,
Denmark
};
enum CIGARETTE //香烟(品牌)
{
PallMall = 20,
Dunhill,
Blends,
BlueMaster,
Prince
};
为了容易编程,我们引入面向对象的设计思想,现在房子这么重要,我们就不以人为本了,哈哈,就以房子为本来进行设计(PS:当然,这个是开玩笑的啦,以房子为本的原因是由于房子位置关系的相对复杂性决定的,我们将房子定下来,并且对每个房子都强制一个唯一的门牌号,这样房子之间的相互关系就很好解决了)。另外,对于此题的问题是问:哪个国家的人养鱼,就是指明了涉及到的两个元素:国籍、宠物。我们写程序的时候就可以根据这两个要点要进行全方位的验证即可。验证的方法相当简单,就是在对关系条件的逐一验证而已,这里仅仅需要我们穷举出不同国籍与宠物之间的排列即可。(为什么是排列而不是组合呢?这里主要考虑到验证房子之间的关系上,因此属于排列问题,而不是组合问题)。即类似于以下:英国人养鸟住二号房子、德国人养马住三号房子这样。。。把所有的情况穷举出来,然后放到条件关系中去验证,满足所有条件的即为解。这样的求解在思路上属于最容易想到且在编程实现上也可行的一个方法,以下,我们先用这个方法来对此题求解【注意:现有后优!!! 一开始就绞尽脑汁去想各种优化和捷径往往使得程序在短时间内无法完成,所谓:过早的优化是万恶之源!!!,因此在短时间内快速找到可行的解决方案并编程处理是首要任务】,若十来分钟过去了,得出了如下的烂尾代码:
typedef unsigned int UINT;
enum HOUSECOLOR //房子颜色
{
XColor = -1, //未知
Red = 0,
Green,
Yellow,
Blue,
White
};
enum BEVERAGE //饮料
{
XDrink = -1, //未知
Milk = 0,
Coffee,
Tea,
Beer,
Water
};
enum NATIONALITY //国籍
{
XNation = -1, //未知
Norway = 0,
Germany,
British,
Sweden,
Denmark
};
enum CIGARETTE //香烟(品牌)
{
XCigar = -1, //未知
PallMall = 0,
Dunhill,
Blends,
BlueMaster,
Prince
};
enum PET //宠物
{
XPet = -1, //未知
Dog = 0,
Fish,
Horse,
Cat,
Bird
};
class House
{
public:
House( const int iHouseNum,
HOUSECOLOR eColor = XColor,
CIGARETTE eCigar = XCigar,
BEVERAGE eDrink = XDrink,
NATIONALITY eNation = XNation,
PET ePet = XPet )
: m_iHouseNum( iHouseNum ),
m_eColor( eColor ),
m_eCigar( eCigar ),
m_eDrink( eDrink ),
m_eNation( eNation ),
m_ePet( ePet )
{
}
~House(){}
public:
//Relationship
bool IsOnLeftSide( const House &objHouse )
{
return 1 == (m_iHouseNum - objHouse.m_iHouseNum);
}
bool IsOnRightSide( const House &objHouse )
{
return -1 == (m_iHouseNum - objHouse.m_iHouseNum);
}
bool IsNeighbour( const House &objHouse )
{
return 1 == abs(m_iHouseNum - objHouse.m_iHouseNum );
}
public:
//Setter and Getter
void SetCigar( const CIGARETTE eCigar ) { m_eCigar = eCigar; }
void SetHouseColor( const HOUSECOLOR eColor ) { m_eColor = eColor; }
void SetBeverage( const BEVERAGE eDrink ) { m_eDrink = eDrink; }
void SetNationality(const NATIONALITY eNation ) { m_eNation = eNation; }
void SetPet( const PET ePet ) { m_ePet = ePet; }
const CIGARETTE GetCigar( void ) const { return m_eCigar; }
const HOUSECOLOR GetHouseColor( void ) const { return m_eColor; }
const BEVERAGE GetBeverage( void ) const { return m_eDrink; }
const NATIONALITY GetNationality( void ) const { return m_eNation; }
const PET GetPet( void ) const { return m_ePet; }
const int GetHouseNum( void ) const { return m_iHouseNum; }
private:
CIGARETTE m_eCigar;
HOUSECOLOR m_eColor;
BEVERAGE m_eDrink;
NATIONALITY m_eNation;
PET m_ePet;
int m_iHouseNum;
};
int main()
{
House rgpHouses[5] = { obj_1, obj_2, obj_3, obj_4, obj_5 };
for (UINT i=1; i<5; ++i) //British
{
for (UINT j=1; j<5; ++j) //Germany
{
for (UINT k=1; k<5; ++k) //Sweden
{
for (UINT l=1; l<5; ++l) //Denmark
{
for (UINT m=1; m<5; ++m) //Fish
{
for (UINT n=1; n<5; ++n) //Horse
{
for (UINT o=1; o<5; ++n) //Cat
{
for (UINT p=1; p<5; ++p) //Bird
{
rgpHouses[i].SetNationality( British );
rgpHouses[i].SetHouseColor( Red );
rgpHouses[j].SetNationality( Germany );
rgpHouses[j].SetCigar(Prince);
rgpHouses[k].SetNationality( Sweden );
rgpHouses[k].SetPet( Dog );
rgpHouses[l].SetNationality( Denmark );
rgpHouses[l].SetBeverage( Tea );
}
}
}
}
}
}
}
}
} //此代码为烂尾代码,循环体内的赋值有明显的问题!!!导致思路无法继续下去!!!
结果发现纯粹使用面向过程的方法,自己竟然无法顺利地解出这道题,在表述复杂性降低的情况下,部分穷举以及方便验证上的复杂度并没有得到实质性地降低。因此得重新寻找比较好的解法。于是我重新阅读题目,这次我进行了另外的尝试,首先将题目条件进行分类,分为:断言条件与关系条件,顾名思义,对于断言条件就是指的类似:什么是什么,什么就什么的这样的条件,例如:英国人住红房子,挪威人养狗等等;而关系条件指的是:谁跟谁有什么关系之类的这样的表示,例如:养马的人住抽Dunhill香烟的人隔壁。根据题目我们可以得到下面的这个分类表格:
断言条件 | 关系条件 |
英国人住红色房子 | 绿色房子在白色房子左面 |
瑞典人养狗 | 抽Blends香烟的人住在养猫的人隔壁 |
丹麦人喝茶 | 养马的人住抽Dunhill香烟的人隔壁 |
绿色房子主人喝咖啡 | 挪威人住蓝色房子隔壁 |
抽Pall Mall香烟的人养鸟 | 抽Blends香烟的人有一个喝水的邻居 |
黄色房子主人抽Dunhill香烟 | |
住在中间房子的人喝牛奶 | |
挪威人住第一间房 | |
抽Blue Master的人喝啤酒 | |
德国人抽Prince香烟 |
得到如下分析:
门牌号: 01 02 04 08 10
0000-0001 0000-0010 0000-0100 0000-1000 0001-0000 (默认未知为:0x00h)国籍: 挪威 德国 英国 瑞典 丹麦
颜色: 黄 蓝 红 绿 白
饮料: 啤酒 水 牛奶 咖啡 茶
香烟牌子: Dunhill Prince Blends BlueMaster PallMall
宠物 Fish Horse Cat Dog Bird
按照:门牌号、国籍、颜色、饮料、香烟牌子、宠物,这样的顺序排列,
例如:英国人住红色房子在2号房间喝水养鸟抽PallMall烟就可以表示为:0x020404021010 (6个字节)
设置条件的方法例如: 0xF2FFFF 表示英国。
门牌号 国籍 颜色 饮料 香烟牌子 宠物
断言条件: 0 1 2 3 4 5英国人住红色房子0x000404000000 (判断方法and 0x000404000000,若为0或还是为其本身都满足条件,否则不满足)
瑞典人养狗0x000800000008 (判断方法and 0x000800000008,若为0或还是为其本身都满足条件,否则不满足)
丹麦人喝茶0x001000100000 同上思路,略之。
绿色房子主人喝咖啡0x000008080000
抽Pall Mall香烟的人养鸟0x000000001010
黄色房子主人抽Dunhill香烟0x000001000100
住在中间房子的人喝牛奶0x040000040000
挪威人住第一间房0x010100000000
抽Blue Master的人喝啤酒0x000000010800
德国人抽Prince香烟0x000200000200
关系条件:
绿色房子在白色房子左面。
抽Blends香烟的人住在养猫的人隔壁
养马的人住抽Dunhill香烟的人隔壁
挪威人住蓝色房子隔壁===>蓝色房子是第二间房(由关系条件转化为了断言条件)
抽Blends香烟的人有一个喝水的邻居
然而,思考到这里有一个新的问题产生了,首先判断用的数据占有6个字节,因此不好使用恰当的内置数据类型进行直接的数据判断(不是大了就是小了),只能使用诸如数组这样的类型来进行相应的判断,而这个时候,就无法使用直接“与”操作来进行判断了。这个时候,反过来考虑一下是否真需要采用“与”操作的这种类型判断呢?!问题的核心究竟是什么呢?!——想要过滤出符合断言条件的不同组合,进而求解出答案!若单纯使用数组判断的话该如何判断比较好呢?再次观察条件我们不难发现,起先我们为了采用“与”判断而对判断数据进行了“特殊”的位设计,现在由于设计的数据大小不太适合内置数据类型的直接操作,而导致“与”操作的优越性无法体现出来,反而成了累赘。当前就需要我们做一个取舍了?!是否要废弃这种按照与操作设计的数据格式!同时对于条件,正向判断的确不是很容易,那么我们是否能够换一个角度去看问题,比如说去逆向判断,例如:英国人住红色的房子,其反面就是英国人不住红色的房子或者红色房子里住的不是英国人!这样一想,我们重新试着去设计新的判断方法及判断数据。还是沿用如下的组合,只不过数据的表现形式变一下,不用采用位来设计了,而是简单的计数标记。
门牌号: 01 02 03 04 5
国籍: 挪威 德国 英国 瑞典 丹麦颜色: 黄 蓝 红 绿 白
饮料: 啤酒 水 牛奶 咖啡 茶
香烟牌子: Dunhill Prince Blends BlueMaster PallMall
宠物 Fish Horse Cat Dog Bird
同时,为了兼顾显示的需要,对应组合表可以设计出如下的对应,显示字符:
//门牌号: 1 2 3 4 5
//国籍: 挪威 德国 英国 瑞典 丹麦
//颜色: 黄 蓝 红 绿 白
//饮料: 啤酒 水 牛奶 咖啡 茶
//香烟牌子: Dunhill Prince Blends BlueMaster PallMall
//宠物: Fish Horse Cat Dog Bird
char *pstrsNationality[5] = { "挪威", "德国", "英国", "瑞典", "丹麦" };
char *pstrsHouseColor[5] = { "黄", "蓝", "红", "绿", "白" };
char *pstrsBeverage[5] = { "啤酒", "水", "牛奶", "咖啡", "茶" };
char *pstrsCigarette[5] = { "Dunhill", "Prince", "Blends", "BlueMaster", "PallMall" };
char *pstrsPet[5] = { "Fish", "Horse", "Cat", "Dog", "Bird" };
有了这个一一对应,我们设计程序的第一道过滤就水到渠成了!
首先根据断言条件进行的第一层过滤设计如下:
设i、j、k、m、n、o分别代表:门牌号、国籍、颜色、饮料、香烟牌子、宠物。
for (UCHAR i=1; i<6; ++i) //门牌号
{
for (UCHAR j=1; j<6; ++j) //国籍
{
for (UCHAR k=1; k<6; ++k) //颜色
{
for (UCHAR m=1; m<6; ++m) //饮料
{
for (UCHAR n=1; n<6; ++n) //香烟牌子
{
for (UCHAR o=1; o<6; ++o) //宠物
{ //门牌号: 1 2 3 4 5
//国籍: 挪威 德国 英国 瑞典 丹麦
//颜色: 黄 蓝 红 绿 白
//饮料: 啤酒 水 牛奶 咖啡 茶
//香烟牌子: Dunhill Prince Blends BlueMaster PallMall
//宠物: Fish Horse Cat Dog Bird
//(否)1.英国人住红色房子
if ( 3==j&&3!=k || 3==k&&3!=j )
continue;
//(否)2.瑞典人养狗
if ( 4==j&&4!=o || 4==o&&4!=j )
continue;
//(否)3.丹麦人喝茶
if ( 5==j&&5!=m || 5==m&&5!=j )
continue;
//(否)4.绿色房子主人喝咖啡
if ( 4==k&&4!=m || 4==m&&4!=k )
continue;
//(否)5.抽Pall Mall香烟的人养鸟
if ( 5==n&&5!=o || 5==o&&5!=n )
continue;
//(否)6.黄色房子主人抽Dunhill香烟
if ( 1==k&&1!=n || 1==n&&1!=k )
continue;
//(否)7.住在中间房子的人喝牛奶
if ( 3==i&&3!=m || 3==m&&3!=i )
continue;
//(否)8.挪威人住第一间房
if ( 1==j&&1!=i || 1==i&&1!=j )
continue;
//(否)9.抽Blue Master的人喝啤酒
if ( 1==m&&4!=n || 4==n&&1!=m )
continue;
//(否)10.德国人抽Prince香烟
if ( 2==j&&2!=n || 2==n&&2!=j )
continue;
//(否)11.蓝色房子是第二间房
if ( 2==i&&2!=k || 2==k&&2!=i )
continue;
printf("%d %d %d %d %d %d \n", i,j,k,m,n,o);
}
}
}
}
}
}
这样很容易就达到了第一层过滤的目的。于此同时,我们还需要对第二层过滤做一个准备!第二层的过滤,即:对应关系条件的过滤,通过上面已有的分析。关系条件为:
1、绿色房子在白色房子左面。
2、抽Blends香烟的人住在养猫的人隔壁。
3、养马的人住抽Dunhill香烟的人隔壁。
4、抽Blends香烟的人有一个喝水的邻居。
对应关系条件判断的方法,我也可以分别从正反两个方面来进行分析:
首先正面分析:以关系条件1为例,绿色房子在白色房子的左面。那么若碰到绿色房子,则必须遍历出另外房子为白色且房号减去绿色房子房号为-1则满足条件。同理对应白色房子,也要遍历出绿色房子且房号减去白房子为1。
然后反面分析:绿色房子不在白色房子左面,还是需要去遍历并判断。
因此得出一个简单的结论,正面或者反面分析对此条件判断难度基本等价。那么我们就采取大家容易思考的正向判断方法,同时在判断前先做好准备工作。首先将不同房间的情况做一个分组。经过第一次的过滤可以完成这个操作:将属于不同的房间号的情况各自放到对应的地方。由于各个房号对应的情况数量无法立即确定,所以用到的容器要求是可变容的。这里我们“偷偷懒”直接用标准C++里面提供的vector容器算了。这样我们需要五个容器对应不同的房间,将属于不同房间的情况一一放入,容器里面的元素我们采用std::string来存储。然后针对不同的情况进行判断。五个容器的处理与一个容器的处理在本质上其实是一样的,因此根据以上的思路,很容易获得如下程序:
//门牌号: 1 2 3 4 5
//国籍: 挪威 德国 英国 瑞典 丹麦
//颜色: 黄 蓝 红 绿 白
//饮料: 啤酒 水 牛奶 咖啡 茶
//香烟牌子: Dunhill Prince Blends BlueMaster PallMall
//宠物: Fish Horse Cat Dog Bird
char *pstrsNationality[5] = { "挪威", "德国", "英国", "瑞典", "丹麦" };
char *pstrsHouseColor[5] = { "黄", "蓝", "红", "绿", "白" };
char *pstrsBeverage[5] = { "啤酒", "水", "牛奶", "咖啡", "茶" };
char *pstrsCigarette[5] = { "Dunhill", "Prince", "Blends", "BlueMaster", "PallMall" };
char *pstrsPet[5] = { "Fish", "Horse", "Cat", "Dog", "Bird" };
int main()
{
size_t tNum_1 = 0;
size_t tNum_2 = 0;
size_t tNum_3 = 0;
size_t tNum_4 = 0;
size_t tNum_5 = 0;
std::vector<std::string> rgpConditions;
//第一阶段循环
for (UCHAR i=1; i<6; ++i) //门牌号
{
for (UCHAR j=1; j<6; ++j) //国籍
{
for (UCHAR k=1; k<6; ++k) //颜色
{
for (UCHAR m=1; m<6; ++m) //饮料
{
for (UCHAR n=1; n<6; ++n) //香烟牌子
{
for (UCHAR o=1; o<6; ++o) //宠物
{ //门牌号: 1 2 3 4 5
//国籍: 挪威 德国 英国 瑞典 丹麦
//颜色: 黄 蓝 红 绿 白
//饮料: 啤酒 水 牛奶 咖啡 茶
//香烟牌子: Dunhill Prince Blends BlueMaster PallMall
//宠物: Fish Horse Cat Dog Bird
//(否)1.英国人住红色房子
if ( 3==j&&3!=k || 3==k&&3!=j )
continue;
//(否)2.瑞典人养狗
if ( 4==j&&4!=o || 4==o&&4!=j )
continue;
//(否)3.丹麦人喝茶
if ( 5==j&&5!=m || 5==m&&5!=j )
continue;
//(否)4.绿色房子主人喝咖啡
if ( 4==k&&4!=m || 4==m&&4!=k )
continue;
//(否)5.抽Pall Mall香烟的人养鸟
if ( 5==n&&5!=o || 5==o&&5!=n )
continue;
//(否)6.黄色房子主人抽Dunhill香烟
if ( 1==k&&1!=n || 1==n&&1!=k )
continue;
//(否)7.住在中间房子的人喝牛奶
if ( 3==i&&3!=m || 3==m&&3!=i )
continue;
//(否)8.挪威人住第一间房
if ( 1==j&&1!=i || 1==i&&1!=j )
continue;
//(否)9.抽Blue Master的人喝啤酒
if ( 1==m&&4!=n || 4==n&&1!=m )
continue;
//(否)10.德国人抽Prince香烟
if ( 2==j&&2!=n || 2==n&&2!=j )
continue;
//(否)11.蓝色房子是第二间房
if ( 2==i&&2!=k || 2==k&&2!=i )
continue;
std::string objStrCondition;
objStrCondition.push_back( i );
objStrCondition.push_back( j );
objStrCondition.push_back( k );
objStrCondition.push_back( m );
objStrCondition.push_back( n );
objStrCondition.push_back( o );
rgpConditions.push_back( objStrCondition );
if ( 1 == i ) ++tNum_1;
if ( 2 == i ) ++tNum_2;
if ( 3 == i ) ++tNum_3;
if ( 4 == i ) ++tNum_4;
if ( 5 == i ) ++tNum_5;
}
}
}
}
}
}
tNum_2 += tNum_1;
tNum_3 += tNum_2;
tNum_4 += tNum_3;
tNum_5 += tNum_4;
//第二层过滤求得结果:
for ( size_t t_1=0; t_1<tNum_1; ++t_1)
{
for (size_t t_2=tNum_1; t_2<tNum_2; ++t_2)
{
for (size_t t_3=tNum_2; t_3<tNum_3; ++t_3)
{
for (size_t t_4=tNum_3; t_4<tNum_4; ++t_4)
{
for (size_t t_5=tNum_4; t_5<tNum_5; ++t_5)
{
UINT uNationality = rgpConditions[t_1][1]+
rgpConditions[t_2][1]+
rgpConditions[t_3][1]+
rgpConditions[t_4][1]+
rgpConditions[t_5][1];
UINT uColor = rgpConditions[t_1][2]+
rgpConditions[t_2][2]+
rgpConditions[t_3][2]+
rgpConditions[t_4][2]+
rgpConditions[t_5][2];
UINT uBeverage = rgpConditions[t_1][3]+
rgpConditions[t_2][3]+
rgpConditions[t_3][3]+
rgpConditions[t_4][3]+
rgpConditions[t_5][3];
UINT uCigar = rgpConditions[t_1][4]+
rgpConditions[t_2][4]+
rgpConditions[t_3][4]+
rgpConditions[t_4][4]+
rgpConditions[t_5][4];
UINT uPet = rgpConditions[t_1][5]+
rgpConditions[t_2][5]+
rgpConditions[t_3][5]+
rgpConditions[t_4][5]+
rgpConditions[t_5][5];
if ( 15 != uNationality ||
15 != uColor || 15 != uBeverage ||
15 != uCigar || 15 != uPet )
continue;
//1、绿色房子在白色房子左面。
bool bV1(false),bV2(false),bV3(false),bV4(false);
if ( 4 == rgpConditions[t_1][2] && 5 == rgpConditions[t_2][2] ||
4 == rgpConditions[t_2][2] && 5 == rgpConditions[t_3][2] ||
4 == rgpConditions[t_3][2] && 5 == rgpConditions[t_4][2] ||
4 == rgpConditions[t_4][2] && 5 == rgpConditions[t_5][2] )
bV1 = true;
//2、抽Blends香烟的人住在养猫的人隔壁。
if ( 3 == rgpConditions[t_1][4] && 3 == rgpConditions[t_2][5] ||
3 == rgpConditions[t_2][4] && 3 == rgpConditions[t_3][5] ||
3 == rgpConditions[t_3][4] && 3 == rgpConditions[t_4][5] ||
3 == rgpConditions[t_4][4] && 3 == rgpConditions[t_5][5] ||
3 == rgpConditions[t_1][5] && 3 == rgpConditions[t_2][4] ||
3 == rgpConditions[t_2][5] && 3 == rgpConditions[t_3][4] ||
3 == rgpConditions[t_3][5] && 3 == rgpConditions[t_4][4] ||
3 == rgpConditions[t_4][5] && 3 == rgpConditions[t_5][4])
bV2 = true;
//3、养马的人住抽Dunhill香烟的人隔壁。
if ( 1 == rgpConditions[t_1][4] && 2 == rgpConditions[t_2][5] ||
1 == rgpConditions[t_2][4] && 2 == rgpConditions[t_3][5] ||
1 == rgpConditions[t_3][4] && 2 == rgpConditions[t_4][5] ||
1 == rgpConditions[t_4][4] && 2 == rgpConditions[t_5][5] ||
2 == rgpConditions[t_1][5] && 1 == rgpConditions[t_2][4] ||
2 == rgpConditions[t_2][5] && 1 == rgpConditions[t_3][4] ||
2 == rgpConditions[t_3][5] && 1 == rgpConditions[t_4][4] ||
2 == rgpConditions[t_4][5] && 1 == rgpConditions[t_5][4])
bV3 = true;
//4、抽Blends香烟的人有一个喝水的邻居。
if ( 2 == rgpConditions[t_1][3] && 3 == rgpConditions[t_2][4] ||
2 == rgpConditions[t_2][3] && 3 == rgpConditions[t_3][4] ||
2 == rgpConditions[t_3][3] && 3 == rgpConditions[t_4][4] ||
2 == rgpConditions[t_4][3] && 3 == rgpConditions[t_5][4] ||
3 == rgpConditions[t_1][4] && 2 == rgpConditions[t_2][3] ||
3 == rgpConditions[t_2][4] && 2 == rgpConditions[t_3][3] ||
3 == rgpConditions[t_3][4] && 2 == rgpConditions[t_4][3] ||
3 == rgpConditions[t_4][4] && 2 == rgpConditions[t_5][3])
bV4 = true;
if ( bV1 && bV2 && bV3 && bV4 )
{
printf("OK! The Result is:\n");
printf("%d %s %s %s %s %s\n",
rgpConditions[t_1][0],
pstrsNationality[rgpConditions[t_1][1]-1],
pstrsHouseColor[rgpConditions[t_1][2]-1],
pstrsBeverage[rgpConditions[t_1][3]-1],
pstrsCigarette[rgpConditions[t_1][4]-1],
pstrsPet[rgpConditions[t_1][5]-1]);
printf("%d %s %s %s %s %s\n",
rgpConditions[t_2][0],
pstrsNationality[rgpConditions[t_2][1]-1],
pstrsHouseColor[rgpConditions[t_2][2]-1],
pstrsBeverage[rgpConditions[t_2][3]-1],
pstrsCigarette[rgpConditions[t_2][4]-1],
pstrsPet[rgpConditions[t_2][5]-1]);
printf("%d %s %s %s %s %s\n",
rgpConditions[t_3][0],
pstrsNationality[rgpConditions[t_3][1]-1],
pstrsHouseColor[rgpConditions[t_3][2]-1],
pstrsBeverage[rgpConditions[t_3][3]-1],
pstrsCigarette[rgpConditions[t_3][4]-1],
pstrsPet[rgpConditions[t_3][5]-1]);
printf("%d %s %s %s %s %s\n",
rgpConditions[t_4][0],
pstrsNationality[rgpConditions[t_4][1]-1],
pstrsHouseColor[rgpConditions[t_4][2]-1],
pstrsBeverage[rgpConditions[t_4][3]-1],
pstrsCigarette[rgpConditions[t_4][4]-1],
pstrsPet[rgpConditions[t_4][5]-1]);
printf("%d %s %s %s %s %s\n",
rgpConditions[t_5][0],
pstrsNationality[rgpConditions[t_5][1]-1],
pstrsHouseColor[rgpConditions[t_5][2]-1],
pstrsBeverage[rgpConditions[t_5][3]-1],
pstrsCigarette[rgpConditions[t_5][4]-1],
pstrsPet[rgpConditions[t_5][5]-1]);
}
}
}
}
}
}
}
虽然获得了正确的结果(如图):
但是整个程序看起来是相当繁琐,而且特别容易弄错(编程的时候由于数字与意义的对应问题),对调试也不利,我们当如何改进呢?首先,一开始我们想到的枚举变量可以保留,用来作为数字与意义的对应,这样编程的时候不容易出错!!!(这点相当重要)。另外对应关系条件的判断上,是否能够采取更加便捷的方式?!这也是接下来优化的目标所在。另外整个程序的算法复杂度为O(n^6),是否能够优化?!在做优化之前,我们回到文章的题目,来稍微总结一下这个思维过程:
1、分清楚数据与条件,将数据用容易且合适的方式表示出来;
2、接下来对条件进行详细地分析,将条件进行细分,同时将复杂条件转换为简单条件;
3、对于不同的条件思考如何使用计算机语言做判断,分别从正向与反向充分思考;
4、综合数据与条件关系,思考解题策略:即算法的初步构思【要求可行即可,不用过于强调优化!】
5、根据算法写出相应的程序;
6、对程序进行算法与编写本身的优化。
7、获取更好更优的程序。
对于此题的优化,放到由一道逻辑推理题衍生的对于实际问题求解的一般思路(续)里面再记录一下。