一个分酒问题的求解

趣味算法之泊松分酒 有一个12品脱(pint)的酒瓶,里面装满葡萄酒,另有8品脱和5品脱的瓶子各一个。问如何从中分出6品脱的酒出来? 传说泊松年轻时成功解决了该问题,勾起了他对数学的兴趣而投身数学研究,因此该问题被称为泊松分酒问题。另外这个问题又被称为分油问题啦,分水问题啦等等。 小学的时候在一本《十万个问什么——数学卷》中看到过这个问题,那本书直接给出了一个解答过程,又没说原理,看得我糊里糊涂。 阅读详情

 

一个分酒问题的求解

流浪狗 2005.3


 这是大部分人都玩过的智力问题,有两个容量为8斤的酒瓶装满酒,另有一个三斤的空酒瓶,利用这三个没有标度的酒瓶将这些酒分给四个人喝,每个人分4斤酒。可能我们也很早的时候就已经得出了正确答案了!可是,我要说的是这的却是一个很有意思的问题,尤其是今天我想用计算机去实现这个问题的解时。可能,接触过游戏的人工智能编程的朋友对这种问题一定不会陌生,因为这跟比如说象棋或者是五子棋里求解一步最佳走法的问题很相似,可以说,你熟悉了这种求解方法,那么要写出一个简单的游戏搜索引擎就不会难道你了。同时,这种问题在面试或者是公务员考试时也会看见他的影子,这就是我提出这个问题的原因!

  现在我们先缝隙一下这个问题该如何实现,首先,我们将他抽象成7个三种类型的瓶子,这样就可以很直观的叠代求解。对于每一时刻的每一状态,都可能出现好几种的倒酒方法,我们从其中选出一种,然后继续构造倒酒方法,直到找到可以倒完酒的方法,如果再找不出可行的到酒方法的情况下,将问题回溯,进行下一个方法的继续求解!下边是倒酒算法的核心函数:


//倒酒的核心函数,产生一个正确的倒酒序列
int GenerateResult(int Box[7],list<MOVE>& list_r)
{
  if(!IsEnd(Box))
  {
    list<MOVE> list_temp;
    int i;
    i=GenAllCurrentMove(Box,list_temp);//产生当前局面的所有走法
    if(i==0)//如果没有走法返回上一层;
    {
      return 0;
    }
    //找出当前局面的所有走法是否能产生正确的倒酒序列
    list<MOVE>::iterator iter=list_temp.begin();
    for(iter;iter!=list_temp.end();iter++)
    {
      list_r.push_back(*iter);
      MakeMove(Box,*iter);
      int res;
      res=GenerateResult(Box,list_r);
      //当前情况下产生以后的所有倒酒方法都不成功,则继续下一个寻求
      if(res==0)
      {
         UnMakeMove(Box,*iter);
         //删除刚才插入的走发
         list_r.pop_back();
      }
      //如果在当前情况下找到解
      else
        return 1;
    }
    //如果当前层没有解,返回上层
     return 0;
  }
  //如果结束
  else
    return 1;
     
}

对于没一个局面的所有走法的求解,可以列举所有的可行的走法:

//产生当前局面所有的倒酒方法,保存在list_temp中
int GenAllCurrentMove(int Box[7],list<MOVE>& list_temp)
{
   int count=0;//倒酒方法的数量
   int max[7]={8,8,3,4,4,4,4};//每个酒瓶或者人的最大容量
   for(int k=0;k<3;k++)
   {
     //当前酒瓶空,继续下一个酒瓶
     if(Box[k]==0)
       continue;
     //循环从左到后找出可以倒酒的方法
     for(int det=0;det<7;det++)
     {
          //如果目标瓶和原瓶相同,继续下一个
          if(det==k)
            continue;
          //瓶之间互相到
          if(det<3)
          {
             //由于0瓶和1瓶是同性质的,所以当一个瓶为空时,不能将另一个瓶里的酒往里面到
             //目标不满
             if(((k==2||det==2)?1:Box[det]>0)&&Box[det]<max[det])
             {
               MOVE pmove;
               //如果目标和原瓶的酒的数量相加大于目标最大的数量
               if(Box[k]+Box[det]>max[det])
               {
                  pmove.Value=max[det]-Box[det];
               }
               //否则
               else
                 pmove.Value=Box[k];
               pmove.From=k;
               pmove.To=det;
               if(IsValid(pmove))
               {
                 list_temp.push_back(pmove);
                  count++;
               }
              }
           }
          //如果直接给人喝的话
          if(det>=3)
          {
                //如果当前这个人还未分够4斤酒
                if(Box[det]+Box[k]<=4)
                {
                   MOVE pmove;
                   pmove.From=k;
                   pmove.To=det;
                   pmove.Value=Box[k];

                   list_temp.push_back(pmove);
                   count++;
                   //从左到右的进行倒酒,碰到当前还一点都未分到酒的人时,此次倒酒结束
                   if(Box[det]==0)
                    break;
                }//if
           }//if
        }
    }
  return count;
}
当然,倒酒的时候,得判断当前到法是否可行,如果来回倒酒或则是倒的时候出现以前倒过的局面,则应该去掉当前的倒酒方法:

//判断当前的走发是否将产生死循环或者将产生一个重复的倒后局面
int IsValid(MOVE& move)
{
  MOVE lastmove;//上一次的倒酒方法
  int temp[7]={8,8,0,0,0,0,0};
  //按倒酒方法倒酒
  MakeMove(Boxes,move);
  list<MOVE>::iterator iter=list_rslt.begin();
  //判断此次倒酒是否将产生与以前重复的局面,从而将次方法剪枝掉
  //如以前出现5 8 3,倒酒后产生5 8 3或者8 5 3
  for(iter;iter!=list_rslt.end();iter++)
  {
    MakeMove(temp,*iter);
    //如果产生以前已经产生的局面
    if((temp[0]==Boxes[0]&&temp[1]==Boxes[1]&&temp[2]==Boxes[2])||
         (temp[1]==Boxes[0]&&temp[0]==Boxes[1]&&temp[2]==Boxes[2]))
    {
      UnMakeMove(Boxes,move);
      return 0;
    }
    lastmove=*iter;
  }

  //恢复倒酒
  UnMakeMove(Boxes,move);

  //如果来回倒酒
  if(lastmove.From==move.To&&lastmove.To==move.From&&lastmove.Value==move.Value)
     return 0;
  return 1;
   
}

//判断是否已经结束
int IsEnd(int Box[7])
{
  //如果三个酒瓶都空了,则表示倒酒结束
  if(Box[0]==0&&Box[1]==0&&Box[2]==0)
      return 1;
  else
      return 0;
}

//按当前的倒酒方法倒酒
void MakeMove(int Box[7],MOVE& move)
{
  Box[move.From]-=move.Value;
  Box[move.To]+=move.Value;
}

//取消上次的倒酒
void UnMakeMove(int Box[7],MOVE& move)
{
  Box[move.From]+=move.Value;
  Box[move.To]-=move.Value;
}

//打印输出
void Print()
{
  int temp[7]={8,8,0,0,0,0,0};
  list<MOVE>::iterator iter=list_rslt.begin();
  cout<<"/*有两个容量为8斤的酒瓶装满酒,另有一个三斤的空酒瓶,"<<"/n"
     <<"利用这三个没有标度的酒瓶将这些酒分给四个人喝,每个人分4斤酒。*//n";
  cout<<"      求解结果/n";
  cout<<"8 8 0    0 0 0 0"<<endl;
  //控制输出条件
  //count1为前一次的所有酒瓶里的酒的和,count2为后一次的
  int  count1,count2;
  count1=16;
  count2=0;
  for(iter;iter!=list_rslt.end();iter++)
  {
    MakeMove(temp,*iter);
    count2=temp[0]+temp[1]+temp[2];
    if(count1!=count2)
    {
      cout<<temp[0]<<" "<<temp[1]<<" "<<temp[2]<<"    "<<temp[3]<<" "<<temp[4]<<" "
         <<temp[5]<<" "<<temp[6]<<endl;
    }
    else
      cout<<temp[0]<<" "<<temp[1]<<" "<<temp[2]<<endl;
    count1=count2;
  }
}

下边是主函数体:

#include<list>


 
struct MOVE{
  int From;//从那儿倒
  int To;//倒去那儿
  int Value;//倒的斤数
  };
 
//产生的走法链表 ,全局变量
list<MOVE> list_rslt;
//放酒的酒瓶Box[0..1]代表装8斤酒的酒瓶,Box[2]代表可以装三斤酒的酒瓶
//Box[3..7]代表喝酒的四个人
int Boxes[7]={8,8,0,0,0,0,0};

//函数声明
int IsEnd(int Box[7]);
int IsValid(MOVE& move);
int GenAllCurrentMove(int Box[7],list<MOVE>& list_temp);
void MakeMove(int Box[7],MOVE& move);
void UnMakeMove(int Box[7],MOVE& move);
int GenerateResult(int Box[7],list<MOVE>& list_r);
void Print();
 
 
void main()
{


  if(GenerateResult(Boxes,list_rslt))
  {
 
    Print();
  }
  else
    cout<<"NO Result!";
}

运行结果如图:

迷宫问题的三种求解方法(递归求解、回溯求解和队列求解) 目录 一、迷宫问题的三种求解方法 递归求解 回溯求解 队列求解 二、华为迷宫问题 一、迷宫问题的三种求解方法 在迷宫问题中,给定入口和出口,要求找到路径。本文将讨论三种求解方法,递归求解、回溯求解和队列求解。 在介绍具体算法之前,先考虑将迷宫数字化。这里将迷宫用一个二维的list存储(即list嵌套在list里),将不可到达的位置用1表示,可到达的位置用0表示,并将已经到过的位置用... 阅读详情

相关推荐

蓝桥杯 泊松分酒 数学分析 解题报告

泊松是法国数学家、物理学家和力学家。他一生致力科学事业,成果颇多。有许多著名的公式定理以他的名字命名,比如概率论中著名的泊松分布。       有一次闲暇时,他提出过一个有趣的问题,后称为:“泊松分酒”。在我国古代也提出过类似问题,遗憾的是没有进行彻底探索,其中流传较多是:“韩信走马分油”问题。       有3个容器,容量分别为12升,8升,5升。其中12升中装满油,另外两个空着。要

colorfulshark 8263

分酒问题(BFS或DFS)

已知有三个容量分别为1斤、7两和3两的并且是没有刻度的酒瓶,1斤的瓶子装满了酒,而7两和三两的瓶子为空。现要求将这些酒分出5两出来。 BFS: #include #include #include #include #include #include #include #include #include #include #define MA

林下的码路 2319

国王试酒问题解法

以前,同学给发了一个号称上海年薪30W的面试题的截图,其中有道国王试酒的问题用了大概一上午的时间给解出来了,那时还是只是解出来,不会有现有的知识把代码写出来,现在感觉水平可以了,所以就把这道题总结一下。 先把题发出来(原图找不到了,只能找一道类似的): 解题过程 这个图片一开始是我上海工作的同学发的一张图片,我一开始也没在意,随手就把它放在了我们的交流群里,然后就炸锅了,所以我也开始想怎么解。一...

weixin_44541606的博客 1278

一般化分酒问题的所有求解(C代码)

DFS 在分酒问题上面,用DFS解决我个人认为是不太好的。主要是因为无法很好地控制一个往回代的过程,也就是如果不仔细加以控制,甚至于即便加以控制,总是有那么一点不确定性将会让程序崩溃。而且在求出解的方面可能会出现比较多的重复解。。我研究了几天的DFS,由于水平原因,还是不得不放弃。改用BFS。 BFS 整体来讲,BFS的算法还算简单化,并且比较容易理解。。废话不多说直接上代码了 (ps:我里面使用...

qq_31630929的博客 813

数学建模分酒问题

分酒问题

分酒问题 三个酒杯A B C分别可装8升、5升、3升酒,开始B C装满了酒,在没有其它工具的情况下,将酒平分

三个酒杯A B C分别可装8升、5升、3升酒,开始B C装满了酒,在没有其它工具的情况下,将酒平分

泊松分酒

泊松分酒是一个著名的智力测试题,也是一个有难度的过程模拟经典案例;1.案例提出:法国数学家泊松(Poisson)曾提出以下分酒趣题: 某人有一瓶12品脱(容量单位)的酒,同时有容积为5品脱与8品脱的空杯各一个,借助这两个空杯,如何将这瓶12品脱的酒平分? 我们要解决一般的平分酒案例: 借助容量分别为bv与cv(单位为整数)的两个空杯,用最少的分倒次数把总容量为偶数a的酒平分,这里正整数bv、cv与偶

double_main的博客 2962

大学生数学建模大赛——分酒问题

论文是分酒问题的C语言程序及详细步骤说明,因此对于数学建模学习非常有用

泊松分酒问题

著名数学家研究过一个有趣的数学问题:某人有12品脱的啤酒一瓶,想从中倒出6品脱,但他没有6品脱的容器,仅有一个8品脱和一个5品脱的容器,怎样才能将啤酒分成两个6品脱呢?其意义是:从12脱的瓶中向8脱的瓶中倒x次,并且将5品脱瓶中的酒向12的瓶中倒y次,最后在12品脱的瓶中剩余6品脱的酒。分别用a、b、c代表12品脱、8品脱和5品脱的瓶子,求出不定方程的整数解,按照不定方程的意义,则倒酒法为:a→b→c→a。依次输入,满瓶的容量 ,第一个空瓶的容量 ,第二个空瓶的容量 ,平分后每个杯子中的品脱数。

2302_76596737的博客 758

HarmonyOS NEXT 开发者预览版Beta招募应知测试

本次HarmonyOS NEXT 开发者预览版 Beta 招募活动为开发者预览版尝鲜升级,主要供开发者进行应用调测使用,除部分系统应用外,其他所有应用将被清除,因此在进行HarmonyOS NEXT 开发者预览版升级前,需进行数据备份。通过应知测试,并提交了参加HarmonyOS NEXT 开发者预览版招募 Beta 活动的报名信息,就会立刻审核通过并且收到新版本。手机在参加HarmonyOS NEXT 开发者预览版 Beta 招募活动前,需要对应机型升级到招募要求对应的基线版本,并预留内存空间。

长久公子的博客 8649

为什么你的Mac蓝牙总断连?深入解析硬件干扰与系统设置避坑

本文深入解析Mac蓝牙频繁断连的原因,包括硬件干扰(如WiFi同频干扰、USB设备电磁干扰)和系统设置问题,并提供详细的优化方案和诊断工具(如blueutil)使用指南,帮助用户有效解决蓝牙稳定性问题。

ik67890123的博客 682

蓝桥-分酒问题

3. 分酒问题 有4个红酒瓶子,它们的容量分别是:9升, 7升, 4升, 2升 开始的状态是 [9,0,0,0],也就是说:第一个瓶子满着,其它的都空着。 允许把酒从一个瓶子倒入另一个瓶子,但只能把一个瓶子倒满或把一个瓶子倒空,不能有中间状态。 这样的一次倒酒动作称为1次操作。 假设瓶子的容量和初始状态不变,对于给定的目标状态,至少需要多少次操作才能实现? 本题就是要求你编程实现最小操作次数的...

无 929

python cartopy绘制扇形区域图/cartopy绘制北极部分区域

实际上,这个问题原因还是由于投影转换的问题,在set_extend时,绘制的上下边界仍然是方形、未被正确投影的边界,与我们的set_boundary存在冲突,最根本的原因还是在于cartopy对于投影计算的一些缺陷。另外我们需要指出的是:**该方法不适用于极地投影,即NorthPolarStereo,由于NorthPolarStereo本身投影特性只需一个参数,本身并不适合。我们绘制极地投影时,同样也是使用set_boundary绘制圆形边界,那么当我们想要绘制扇形时,可以通过。请根据喜好自行选择,

weixin_43750300的博客 3212

旧题新解-用动态规划解决四人分酒问题

在大学c语言课上要求讲回溯法解题报告的时候搜到的这题,当时我看的是一篇09年的解题报告,用的是回溯法解决。不过实际上,如果只输出最终的最优解的话,回溯法效率有些低(我看的那个版本是运行了8秒。)。其实如果用动态规划的方式的话,可以很快解决这个问题(只输出结果的话用我的电脑跑大概是0.03秒,输出路径的话是0.24秒)。 其实宽搜更好,真的。

god_of_alb的博客 635

OTN设备核心知识速记

本文章为工作笔记,若有错误,还需大家指正。不定期更新。

Hiddengold的博客 2648

crazyswarm无人机集群搭建笔记(一)资料索引

crazyswarm无人机搭建笔记:资料收集

Coco_range的博客 806

广度搜索——分酒问题(最优解) 收藏

 问题描述:已知有三个容量分别为3千克、5千克和8千克的并且是没有刻度的酒瓶,3千克和5千克的瓶子均装满了酒,而8千克的瓶子为空。现要求仅用这三个酒瓶将这些酒均分为两个4千克并分别装入5千克和8千克的瓶子中。分析:之前分析过关于分酒问题的一般情况,牵涉到搜索问题的一般算法:(1)回溯算法:不能得到最优解(2)深度搜索算法:不能得到最优解(3)广度搜索算法:可以得到最优解

乐吾天 1393

保姆级教程:用高云GW2A开发板搭建RISC-V软核处理器(含DDR3内存优化配置)

本文提供了一份详细的保姆级教程,指导开发者使用高云GW2A-LV18PG256C8 FPGA开发板搭建RISC-V软核处理器系统。教程涵盖了从开发环境搭建、PicoRV32软核集成,到关键的DDR3内存控制器配置与优化,并深入讲解了时序约束与系统调试方法,帮助初学者和爱好者实现从逻辑设计到软硬件协同的深度实践。

2a4s6d8f0g的博客 566

数学|泊松分酒问题蕴藏的数学知识

欢迎点击「算法与编程之美」↑关注我们!本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章。问题描述相信很多人都听说过泊松分酒的问题,泊松在一次闲暇...

算法与编程之美 2548

因素空间与可拓学的互补性分析及问题处理融合模型

随着知识与信息数量的快速增长以及信息获取便利性的增加,企事业组织所面临的内外部环境更加复杂,业务问题呈现非线性、不确定性、多维化和实时交互性等特点。业务环境的高度不确定性和面临问题的开放性,人工智能与问题处理的深入研究迫切需要有一种自觉地为信息科学与智能科学服务的定性定量相结合的智能数学模型。经典数学适于描述物质世界的数量与空间关系,智能数学必须能描述信息拓展与智能机理。

weixin_70923796的博客 261

基于matlab的LFM脉冲压缩仿真

传统雷达测距是通过发射点频脉冲串实现的,通过发射脉冲与回波的时延确定目标的距离。此类方法易于实现,对接收机的要求较低。一般要获取更好的距离分辨力,需要增加发射信号的带宽,对于点频脉冲信号即需要减小发射脉冲宽度;而为获取更大的探测距离,提升雷达威力,则需要发射更大占空比的脉冲串。由于点频信号的时宽带宽积约等于1,因此不能同时增加信号的时宽和带宽。若要保持距离分辨力,增大占空比就需要减少雷达的脉冲重复周期(PRT)。这样会减小雷达的无模糊探测距离,也会造成目标回波频繁被遮挡,损失雷达威力。

平时工作较为繁忙,私信消息一般晚上回复,谢谢大家~~ 1770

广度搜索——分酒问题(最优解)

问题描述:已知有三个容量分别为3千克、5千克和8千克的并且是没有刻度的酒瓶,3千克和5千克的瓶子均装满了酒,而8千克的瓶子为空。现要求仅用这三个酒瓶将这些酒均分为两个4千克并分别装入5千克和8千克的瓶子中。 分析:之前分析过关于分酒问题的一般情况,牵涉到搜索问题的一般算法:(1)回溯算法:不能得到最优解(2)深度搜索算法:不能得到最优解(3)广度搜索算法:可以得到最优解

ClearRiver 's Style 2469

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

分酒问题

//分酒/*有四个人喝酒,有两个八两装满酒的瓶,只有一个三两的杯子。他们要平分这些酒,但是不能借用其它的工具,你觉得应该怎么做?*/#includeint position[6354][8];//状态集long ptop=0;//后来实验的结果是,在求解的过程中可能出现6354种状态int object[7]={8,8,0,0,0,0,0};//0,1瓶,2杯,3~6人,写在一起只是为了便于搜索i

enshiwzw的专栏 1245

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

回溯——四人分酒问题 收藏

http://blog.chinaunix.net/u/30202/showart_391485.htmlhttp://bjydfq.itpub.net/post/7617/42768题目:两瓶 8 两的酒,一个 3 两的杯子,瓶和杯子都没有刻度,如何将酒平均分给四个人喝?也就是每个人分 4 两酒,也没什么限制条件,当然,喝过的酒不能再吐出来(: > )。 思路:回溯算法,递归 对

乐吾天 1061

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

状态图----分酒问题

题目描述: 有4个红酒瓶子,它们的容量分别是:9升, 7升, 4升, 2升 开始的状态是 [9,0,0,0],也就是说:第一个瓶子满着,其它的都空着。 允许把酒从一个瓶子倒入另一个瓶子,但只能把一个瓶子倒满或把一个瓶子倒空,不能有中间状态。 这样的一次倒酒动作称为1次操作。 假设瓶子的容量和初始状态不变,对于给定的目标状态,至少需要多少次操作才能实现? 本题就是...

weixin_30852419的博客 328
下一篇: 类似QQ界面的实现
jamiandy
博客等级 码龄22年 0粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值