`
ldb19890624
  • 浏览: 243912 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

算法系列之七:爱因斯坦的思考题(下)

 
阅读更多

CheckGroupRelation()函数需要根据当前组group的位置进行适当的处理,如果当前组是第一个组或最后一个组,则group的相邻组只有一个,就是最靠近group的组,其它情况下group的相邻组都是两个。CheckGroupRelation()函数的实现如下:

162bool CheckGroupRelation(GROUP *groups, int groupIdx, ITEM_TYPE type, int value)

163{

164 if(groupIdx == 0)

165 {

166 if(GetGroupItemValue(&groups[groupIdx + 1], type) != value)

167 {

168 return false;

169 }

170 }

171 else if(groupIdx == (GROUPS_COUNT - 1))

172 {

173 if(GetGroupItemValue(&groups[groupIdx - 1], type) != value)

174 {

175 return false;

176 }

177 }

178 else

179 {

180 if( (GetGroupItemValue(&groups[groupIdx - 1], type) != value)

181 && (GetGroupItemValue(&groups[groupIdx + 1], type) != value) )

182 {

183 return false;

184 }

185 }

186

187 return true;

188}

最后是枚举算法部分的说明。前面系列文章中多次使用穷举法解决问题,但都是一维线性组合的枚举,本题则有些特殊,因为需要对不同类型的元素分别用穷举法进行枚举,因此不是简单的线性组合。本文算法采用的枚举方法是对不同类型的元素分别用穷举法进行枚举,然后再进行组合,这个组合不是线性关系的组合,而是类似阶乘的几何关系的组合。具体思路就是按照group中的元素顺序,首先对房子根据颜色组合进行穷举,每得到一组房子颜色组合后,就在此基础上对住在房子里的人的国籍进行穷举,在房子颜色和国籍的组合结果基础上,在对饮料类型进行穷举,依次类推,直到穷举完最后一种类型得到完整的穷举组合。

现在来具体推算一下穷举的过程,首先是对房子颜色进行穷举。因为是5种颜色的不重复组合,因此应该有5!= 120个颜色组合结果,但是根据线索4“绿房子紧挨着白房子,在白房子的左边”,相当于绿房子和白房子有稳定的绑定关系,实际就只有4!= 24个颜色组合结果。接下来对24个房子颜色组合结果中的每一个结果再进行住户国籍的穷举,理论上国籍也有5!= 120个结果,但是根据线索9“挪威人住在第一个房子里面”,相当于固定第一个房子住得人始终是挪威人,因此就只有4!= 24个国籍组合结果。穷举完房子颜色和国籍后就已经有24 x 24 = 576个组合结果了,接下来需要对这576个组合结果中的每一个结果再进行饮料类型的穷举,理论上饮料类型也有5!= 120个结果,但是根据线索8“住在中间那个房子里的人喝牛奶”,相当于固定了一个饮料类型,因此也只有4!= 24个饮料组合类型。穷举完饮料类型后就得到了576 x 24 = 13824个组合结果,接下来对13824个组合结果中的每一个结果再进行宠物种类的穷举,这一步没有线索可用,共有5!= 120个结果。穷举完宠物种类后就得到了13824 x 120 = 1658880个组合结果,最后对1658880个组合结果中的每一个结果再进行香烟品牌的穷举,这一步依然没有线索可用,共有5!= 120个结果。穷举完香烟品牌后就得到了全部组合共1658880 x 120 = 199065600个结果,剩下的事情就是对这将近2亿个结果进行判断。

以下就是对房子颜色进行穷举的算法实现:

369/* 遍历房子颜色*/

370void EnumHouseColors(GROUP *groups, int groupIdx)

371{

372 if(groupIdx == GROUPS_COUNT) /*递归终止条件*/

373 {

374 ArrangeHouseNations(groups);

375 return;

376 }

377

378 for(int i = COLOR_BLUE; i <= COLOR_YELLOW; i++)

379 {

380 if(!IsGroupItemValueUsed(groups, groupIdx, type_house, i))

381 {

382 groups[groupIdx].itemValue[type_house] = i;

383 if(i == COLOR_GREEN) //应用线索(4):绿房子紧挨着白房子,在白房子的左边;

384 {

385 groups[++groupIdx].itemValue[type_house] = COLOR_WHITE;

386 }

387

388 EnumHouseColors(groups, groupIdx + 1);

393 }

394 }

395}

EnumHouseColors()函数每得到一个房子颜色的穷举结果,就调用ArrangeHouseNations()函数进行进一步处理。ArrangeHouseNations() 函数做的事情就是继续枚举住在每个房子里的人的国籍,但是在这之前,先要根据已知条件进行预处理。比如已知规则(9)的描述:挪威人住在第一个房子里,就可以将group[0]的国籍锁定为NATION_NORWAY,从而减少一层遍历。

361void ArrangeHouseNations(GROUP *groups)

362{

363 /*应用规则(9):挪威人住在第一个房子里面;*/

364 groups[0].itemValue[type_nation] = NATION_NORWAY;

365 EnumHouseNations(groups, 1); /*从第二个房子开始*/

366}

实际上,真正的遍历国籍过程是在EnumHouseNations()函数中完成。EnumHouseNations()函数每得到一个完整的房子与国籍关系,就调用ArrangePeopleDrinks()函数进一步遍历每个人喝的饮料类型。

341/*遍历国家*/

342void EnumHouseNations(GROUP *groups, int groupIdx)

343{

344 if(groupIdx == GROUPS_COUNT) /*递归终止条件*/

345 {

346 ArrangePeopleDrinks(groups);

347 return;

348 }

349

350 for(int i = NATION_NORWAY; i <= NATION_GERMANY; i++)

351 {

352 if(!IsGroupItemValueUsed(groups, groupIdx, type_nation, i))

353 {

354 groups[groupIdx].itemValue[type_nation] = i;

355

356 EnumHouseNations(groups, groupIdx + 1);

357 }

358 }

359}

360

在每一组房子颜色和(住房客)国籍的对应关系上(根据前面的分析,将会有24 x 24 = 576组对应关系),ArrangePeopleDrinks()函数负责进一步排定它们和饮料类型的关系。ArrangePeopleDrinks()函数直接调用EnumPeopleDrinks()函数进行饮料类型的枚举,规则(8):“住在中间那个房子里的人和牛奶”直接体现在EnumPeopleDrinks()函数中:

313void EnumPeopleDrinks(GROUP *groups, int groupIdx)

314{

315 if(groupIdx == GROUPS_COUNT) /*递归终止条件*/

316 {

317 /*应用规则(8):住在中间那个房子里的人喝牛奶;*/

318 if(groups[2].itemValue[type_drink] == DRINK_MILK)

319 {

320 ArrangePeoplePet(groups);

321 }

322 return;

323 }

324

325 for(int i = DRINK_TEA; i <= DRINK_MILK; i++)

326 {

327 if(!IsGroupItemValueUsed(groups, groupIdx, type_drink, i))

328 {

329 groups[groupIdx].itemValue[type_drink] = i;

330 EnumPeopleDrinks(groups, groupIdx + 1);

331 }

332 }

333}

在确定完饮料类型后(根据前面的分析,将会得到576 x 24 = 13824组结果),就要进一步遍历每个人养的宠物(别忘了,鱼也算宠物)。题目没有对宠物提供更多的线索,因此ArrangePeoplePet()函数就是简单的遍历全部宠物组合,得到120种人和宠物的组合:

288void EnumPeoplePats(GROUP *groups, int groupIdx)

289{

290 if(groupIdx == GROUPS_COUNT) /*递'b5?归'b9?终'd6?止'd6?条'cc?件'bc?*/

291 {

292 ArrangePeopleCigert(groups);

293 return;

294 }

295

296 for(int i = PET_HORSE; i <= PET_DOG; i++)

297 {

298 if(!IsGroupItemValueUsed(groups, groupIdx, type_pet, i))

299 {

300 groups[groupIdx].itemValue[type_pet] = i;

301

302 EnumPeoplePats(groups, groupIdx + 1);

303 }

304 }

305}

人和宠物的关系也遍历完成以后(根据前面的分析,将会得到13824 x 120 = 1658880组结果),就要最后确定每个人抽的香烟类型。关于人和香烟的关系,题目中也没有给出更多的线索,因此ArrangePeopleCigert()函数仅仅是调用EnumPeopleCigerts()函数完成120种人和香烟的组合:

264void EnumPeopleCigerts(GROUP *groups, int groupIdx)

265{

266 if(groupIdx == GROUPS_COUNT) /*递归终止条件*/

267 {

268 DoGroupsfinalCheck(groups);

269 return;

270 }

271

272 for(int i = CIGARET_BLENDS; i <= CIGARET_BLUEMASTER; i++)

273 {

274 if(!IsGroupItemValueUsed(groups, groupIdx, type_cigaret, i))

275 {

276 groups[groupIdx].itemValue[type_cigaret] = i;

277

278 EnumPeopleCigerts(groups, groupIdx + 1);

279 }

280 }

281}

每当人和香烟的关系也遍历完成后,就调用DoGroupsfinalCheck()对完整的组合进行检查,检查主要是基于Bind和Relation两种类型的线索进行检查(另一种类型的线索已经在遍历的过程中体现)。根据本文前面给出的bind和Relation两种类型的数学模型,DoGroupsfinalCheck()函数的实现非常简单,就是逐个调用前面给出的CheckGroupRelation()函数和CheckGroupBind()函数对两类关系逐个对比检查,此处就不再列出代码。

图(3)演示了在每一轮穷举过程中正确结果组合出来的过程,在我的Intel P8600 CPU上,使用单核完成全部枚举和判断共耗时26秒,得到符合全部线索的答案只有一个,就是本文开始给出的示例。

图(3)每一轮穷举过程中正确结果组合出来的过程

分享到:
评论

相关推荐

    算法导论及课后习题与思考题答案.pdf

    本书自出版以来,一直被视为学习算法设计与分析的最佳指南之一,其深入浅出地介绍了算法的基本概念和技术,并通过丰富的实例帮助读者理解这些理论的实际应用。 #### 第二版特点 第二版相比第一版进行了大量的修订...

    算法导论及课后习题与思考题答案

    数据结构 C语言 计算机 算法导论 及课后习题与思考题答案

    算法系列之二十:计算中国农历

    算法系列之二十:计算中国农历

    算法导论(美国版)课后习题与思考题答案合集

    **Instructor’s Manual**是为教师提供的辅助材料,包含了针对《算法导论》第二版课后习题和思考题的详细解答。这些解答不仅有助于教师更好地准备课程,还能帮助学生深入理解复杂的概念和技术。以下是各章节的主要...

    算法导论课后习题与思考题答案合集

    2. 习题与思考题答案:文档提到了这些答案是针对算法导论的课后习题的,这类资源对于学生来说非常宝贵,能够帮助他们检验对课程内容的理解和掌握。 3. 第二版(Second Edition):文档指出答案集是配合算法导论第二...

    算法导论课后习题及思考题合集

    ### 算法导论课后习题及思考题合集 #### 一、绪论 《算法导论》作为计算机科学领域内的一本经典教材,由Thomas H. Cormen等四位作者共同编著,第二版自2002年出版以来受到了广泛的认可与好评。该书不仅详细介绍了...

    Java算法集题大全.zip

    Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法...

    算法导论 教师手册 英文版 课后思考题答案

    - **课后思考题答案**:帮助学生巩固对算法基础知识的理解,并通过具体例子加深对算法分析技巧的认识。 ##### 2. **第二章:函数的增长** - **讲座笔记**:探讨了不同函数的增长速度,讲解了大O记号、Ω记号和Θ...

    P3_算法题备考:数组代码题.xmind

    P3_算法题备考:数组代码题.xmind

    算法导论课后习题与思考题答案

    根据提供的信息,“算法导论课后习题与思考题答案”主要关注于《算法导论》一书中的课后习题解答。本书是计算机科学领域内非常经典的一部教材,由Thomas H. Cormen、Charles E. Leiserson、Ronald L. Rivest以及...

    算法:C语言实现(第1~4部分)答案

    在本资源中,我们主要关注的是使用C语言实现算法的解答,这涵盖了算法的第1到第4个部分。C语言是一种广泛用于系统编程、应用编程、嵌入式系统以及编写算法实现的强大编程语言。其简洁性和高效性使得它成为学习和理解...

    算法导论思考题

    由于原文件中的部分内容由于OCR技术识别误差,有些文字未能准确呈现,但整体上不影响我们对《算法导论思考题》核心知识点的理解和把握。以上内容是基于文件提供的信息提炼和总结的,确保了知识点的准确性和专业性。

    算法导论第十七章习题解答

    第十七章通常涉及的是图算法,这部分内容在实际编程中有着广泛的应用,比如网络路由、最短路径计算、任务调度等。以下是对第十七章习题解答的详细解析。 1. **图的基本概念**:图是由顶点(节点)和边构成的数据...

    算法导论中文第三版习题答案

    2. 设计和实现算法:书中提供了许多经典的算法,如快速排序、归并排序、二分查找等,读者需要学会如何编写这些算法的代码。 3. 数学应用:部分习题涉及到概率、图论、组合数学等领域的知识,这些是理解和设计某些...

    算法导论大部分课后习题与思考题答案合集

    2. 习题与思考题答案合集:文件表明,该pdf包含了上述教材《算法导论》(第二版)中部分习题和思考题的解答。这些答案的提供可以帮助学生更好地理解和掌握教材中的概念,是学习者的重要学习资源。 3. 网上比较全的...

Global site tag (gtag.js) - Google Analytics