泊松是法国数学家、物理学家和力学家。他一生致力科学事业,成果颇多。有许多著名的公式定理以他的名字命名,比如概率论中著名的泊松分布。
有一次闲暇时,他提出过一个有趣的问题,后称为:“泊松分酒”。在我国古代也提出过类似问题,遗憾的是没有进行彻底探索,其中流传较多是:“韩信走马分油”问题。
有3个容器,容量分别为12升,8升,5升。其中12升中装满油,另外两个空着。要求你只用3个容器操作,最后使得某个容器中正好有6升油。
下面的列表是可能的操作状态记录:
12,0,0
4,8,0
4,3,5
9,3,0
9,0,3
1,8,3
1,6,5
每行3个数据,分别表示12,8,6升容器中的油量
第一行表示初始状态,第二行表示把12升倒入8升容器后的状态,第三行是8升倒入5升,...
当然,同一个题目可能有多种不同的正确操作步骤。
本题目的要求是,请你编写程序,由用户输入:各个容器的容量,开始的状态,和要求的目标油量,程序则通过计算输出一种实现的步骤(不需要找到所有可能的方法)。如果没有可能实现,则输出:“不可能”。
例如,用户输入:
12,8,5,12,0,0,6
用户输入的前三个数是容器容量(由大到小),接下来三个数是三个容器开始时的油量配置,最后一个数是要求得到的油量(放在哪个容器里得到都可以)
则程序可以输出(答案不唯一,只验证操作可行性):
12,0,0
4,8,0
4,3,5
9,3,0
9,0,3
1,8,3
1,6,5
每一行表示一个操作过程中的油量状态。
注意:
请仔细调试!您的程序只有能运行出正确结果的时候才有机会得分!
解题思路:
开始的时候只是以为就是个深搜的题目结果深搜下去好像不知道循环的过程,其实之后看别人的解题报告的时候好像也还是行的,一共有六种情况,然后对这六种进行编立就行了(注意边界条件,详情:网址:http://blog.csdn.net/keepthinking_/article/details/8755319),但是这样好像实现起来有点麻烦
下面代码的思路是:为了避免重复,也就是可能在倒的时候倒过来倒过去又回来了,但是还没有遍历完所有的方式,也就是说漏掉了很多种方式就跳出“不可能”了,所以说遍历的时候还是有技巧有顺序的,这应该也是这道题的精髓所在和难点吧,所以说遍历的顺序是什么呢,总该有个原则吧?是的,必须有原则,要不然这道题好像就做不出来了。
那么原则是什么呢?就是在倒的时候一定保持是从大容器中倒到次大的,次大的倒向最小的,如果分别用A,B,C来表示的话就是A->B,B->C,C->A按照这个顺序还是有条件的,如果不加条件,只是顽固的按照这个顺序遍历的话多数是倒不出来的,开始的时候就是吃了这个亏,首先应该看B是不是有,如果没有的话就要从A里往B里倒了,这样在为往C里倒做铺垫,当然C如果是满了的话,还是要倒到A里,这样的话实现起来就好了,题目OK。。。
至于为什么是从大的往小的倒,这个我现在还真的是说不太清楚,但是好像用别的方式到更没有原则,另外我感觉这样的话因为用小的作为量度可以把大的一步步分割,相当于把大的切小了在放,这样的话离出现想要的结果就更进一步了,所以。。。剩下的以后再研究,总感觉这道题还有点什么漏洞,并且能够理论证明出来,但是能力好像真的有限。
PS:网上答案好像只有几个版本哦,弱弱的问一句这样机械有意思么
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; int path[100][3],coun=0; int vola,volb,volc,nowa,nowb,nowc,pur; void AtoB(){ if(volb-nowb<=nowa){//b中的可以全部放满 int temp = volb-nowb; nowb=volb; nowa-=temp; } else{ int temp = nowa; nowa = 0; nowb +=temp; } } void BtoC(){ if(volc-nowc<=nowb){//c中的可以全部放满 int temp = volc-nowc; nowc=volc; nowb-=temp; } else{ int temp = nowb; nowb = 0; nowc +=temp; } } void CtoA(){ if(vola-nowa<=nowc){//a中的可以全部放满 int temp = vola-nowa; nowa=vola; nowc-=temp; } else{ int temp = nowc; nowc = 0; nowa +=temp; } } int main (){ scanf("%d,%d,%d,%d,%d,%d,%d",&vola,&volb,&volc,&nowa,&nowb,&nowc,&pur); if(nowa==pur||nowb==pur||nowc==pur) { printf ("%d,%d,%d ",nowa,nowb,nowc); return 0; } while(1){ path[coun][0] = nowa,path[coun][1] = nowb,path[coun++][2] = nowc;//将当前的路径数组填充 if(nowb==0) AtoB(); //b中没有了 else if(nowc==volc) CtoA(); //c中满了 else BtoC(); if(nowa==pur||nowb==pur||nowc==pur){//如果出现了目标 for(int i=0;i<coun;i++) printf ("%d,%d,%d ",path[i][0],path[i][1],path[i][2]); printf ("%d,%d,%d ",nowa,nowb,nowc); return 0; } for(int i=0;i<coun;i++ ){ if( nowa==path[i][0]&&nowb==path[i][1]&&nowc==path[i][2]){ //如果出现了路径中的某一步,说明已经出现循环圈了 printf("不可能"); return 0; } } } return 0; }