倒水问题

二.倒水问题

这个题目的版本非常之多,有微软版的,腾讯版的,新浪版的等等,最常见的是给你一个容量为5升的桶和一个容量为3升的桶,水不限使用,要求精确得到4升水。

解法肯定有很多,可以用宽度优先搜索(BFS),也可以用穷举法。穷举法实现比较方便,其基本思想是用:用小桶容量的倍数对大桶的容量进行取余。比如3升的桶和5升的桶得到4升水可以这样做:

3 % 5 = 3

6 % 5 = 1

9 % 5 = 4

成功得到4升水。(PS:上面的过程用如何用文字描述了?)

同样,用7升的桶和11升的桶得到2升水可以这样做:

7 % 11 = 7

14 % 11 = 3

21 % 11 = 10

28 % 11 = 6

35 % 11 = 2

成功得到2升水。

哈哈,有了这个基本思想在用笔算答案时简直是遇神杀神,遇佛杀佛,又方便又快速!如果要求用程序来实现如何做了?easy,将倒水问题的基本思想用易于编程的话来翻译下——不断用小桶装水倒入大桶,大桶满了立即清空,每次判断下二个桶中水的容量是否等于指定容量。有了这个倒水问题的编程指导方针后代码非常容易写出:

  1. //热门智力题 - 打水问题  
  2. //基本思想:用小桶容量的倍数对大桶的容量进行取余。  
  3. //指导方针:不断用小桶装水倒入大桶,大桶满了立即清空,  
  4. //每次判断下二个桶中水的容量是否等于指定容量。  
  5. #include<iostream>  
  6. #include <vector>  
  7. #include<string>  
  8. using namespace std;  
  9. const string OPERATOR_NAME[7] = {  
  10.     "装满A桶","装满B桶",  
  11.     "将A桶清空","将B桶清空",  
  12.     "A桶中水倒入B桶","B桶中水倒入A桶",  
  13.     "成功"  
  14. };  
  15. int main()  
  16. {  
  17.     cout<<"热门智力题 - 打水问题"<<endl;  
  18.     cout<<"  --by MoreWindows( http://blog.csdn.net/MoreWindows )--\n"<<endl;  
  19.   
  20.     int    a_volume, b_volume, goal_volume;  
  21.     vector<string> record;       //记录操作步数  
  22.     int    ai;  
  23.     int    i, a_water, b_water;  
  24.   
  25.     cout<<"请输入A桶容量,B桶容量,目标容量:";  
  26.     cin>>a_volume>>b_volume>>goal_volume;  
  27.     a_water = b_water = 0; //A桶,B桶中有多少升水  
  28.     char szTemp[30];  
  29.     while (true)  
  30.     {  
  31.         if (a_water == 0)//A桶没水,就装满水  
  32.         {  
  33.             a_water = a_volume;  
  34.             sprintf(szTemp, "         A:%d  B:%d", a_water, b_water);   
  35.             record.push_back(OPERATOR_NAME[0] + szTemp);//fill A  
  36.         }  
  37.         else  
  38.         {  
  39.             //如果A桶的水比(B桶容量-B桶的水)要多  
  40.             if (a_water > b_volume - b_water)  
  41.             {  
  42.                 //A桶的水==A桶的水+B桶的水-B桶容量  
  43.                 a_water = a_water + b_water- b_volume;  
  44.                 b_water = b_volume;      //B桶的水装满了  
  45.                 sprintf(szTemp, "  A:%d  B:%d", a_water, b_water);   
  46.                 record.push_back(OPERATOR_NAME[4] + szTemp);//A->B     
  47.                 if (a_water == goal_volume)  
  48.                     break;  
  49.                 b_water = 0;            //将B桶清空  
  50.                 sprintf(szTemp, "       A:%d  B:%d", a_water, b_water);   
  51.                 record.push_back(OPERATOR_NAME[3] + szTemp);  
  52.             }  
  53.             else  
  54.             {  
  55.                 //B桶的水==A桶的水+B桶的水  
  56.                 b_water += a_water;   
  57.                 a_water = 0;  
  58.                 sprintf(szTemp, "  A:%d  B:%d", a_water, b_water);  
  59.                 record.push_back(OPERATOR_NAME[4] + szTemp);//A->B  
  60.                 if (b_water == goal_volume)   
  61.                     break;  
  62.             }  
  63.         }  
  64.     }  
  65.     record.push_back(OPERATOR_NAME[6]); //success  
  66.     cout<<"\n---------------------------------------------------"<<endl;  
  67.     cout<<"一个可行的倒水方案如下"<<endl;  
  68.     vector<string>::iterator pos;  
  69.     for (pos = record.begin(); pos != record.end(); pos++)  
  70.         cout<<*pos<<endl;  
  71.     cout<<"---------------------------------------------------"<<endl;  
  72.     return 0;  
  73. }  

程序运行结果如下:

注意这里只是给出一个可行的倒水方案,不一定是最优解。另外倒水问题要注意下像2升的桶和4升的桶得到3升水这种不可解的情况,这种不可解情况在用本文中对倒水问题所总结的基本思想计算时会得到循环数列。其根本原因是二个桶容量的最大公约数无法被目标容量所整除,如6升的桶和9升的桶无法得到2升水是因为69的最大公约数是3GCD(69)=33无法整除2

倒水问题也也容易推广到多个桶的情况,分析方法和上文差不多,这里就不再赘述了。

 

过桥问题和倒水问题就讲解到此,相信以后这二类问题都不会再困扰大家了^_^。

posted on 2013-07-11 11:48  虚若怀谷  阅读(328)  评论(0)    收藏  举报

导航