8升水等分问题

题目

有一个装满8升水的杯子,另外有两个5升和3升的空杯子。现在想通过这三个杯子把8升水等分,不得标记刻度和使用其他器具。如何做到均分者8升水,请输出步骤。

我的心路历程

如何才能把这些步骤转换为可计算的状态,然后穷举出来。我思来想去死活想不出办法,这个杯子倒来倒去怎么才能统计呢?脑袋一片空白,比如第一步怎么倒,然后第二步又该怎倒,怎么记住这些状态,然后又如何进入下一个状态?心里知道这一定还是得用DFS(深度优先搜索),到底该怎么穷举,全排列又不是。。。

没办法,只能求助度娘,看看思路到底要怎么整。然后根据别人的分析,我重新整理了一下思路,每一步的状态取决于每一次的倒水方式,所以要确定每一步倒水的方式有几种便可以确定每一步的状态。倒水的方式有几种呢?

  1. 八升的杯子往其他两个杯子倒水
  2. 五升的杯子往其他两个杯子倒水
  3. 三升的杯子往其他两个杯子倒水

由此,可以看出可以组合排列六种倒水方式,递归的每一步有六种状态。写到这里,我已经明白怎么去处理这些状态了,怎么去DFS了,这个算法实在太熟了,只是不知道如何去量化这些状态。每一步都是有限的状态,下一步继续六种状态的轮询,减去已经处理的状态和不合理的状态。

总结代码思路

最后,整理出我的代码思路,仅供参考。
1. DFS的主体,每一步遍历6种倒水方式,并存储当前状态到一个队列。退出条件是440。
2. 倒水方式排除:空杯不能倒出去,慢了倒不进来,自己不能倒给自己。
3. 每一步的状态用整数表示,比如,第一步800,最后一步440.

show源码

#include
#define rint register int
#define SIZE (801)
#define END  (440)         //DFS终止条件
int box[3] = {8, 0, 0};    //杯子初始状态
int val[3] = {100, 10, 1}; //把杯子的状态转化为整数,方便统计
int ful[3] = {8, 5, 3};    //杯子的容量
int vis[SIZE];             //当前状态是否访问过了
int Q[SIZE];               //存储每一步的状态
int step = 0;
int ansCnt = 0;            //总共有多少个方式的到答案
//这次倒水方式是否有效合理
int isVaildAction(int from, int to)
{
       if(from != to && box[from] != 0 && box[to] != ful[to])
             return 1;
       return 0;
}
//计算当前的状态
int getBoxState()
{
       int sum = 0;
       for(rint i = 0; i < 3; i++)
       {
             sum += box[i]*val[i];//比如800,530
       }
       return sum;
}
//计算下一步的状态
int getNextState(int from, int to)
{
       int capa = ful[to] - box[to];
       if(box[from] >= capa)
       {
             box[from] -= capa;
             box[to] = ful[to];
       }
       else
       {
             box[to] += box[from];
             box[from] = 0;
       }
       return getBoxState();
}
//恢复到上一步的状态
int restoreState(int index)
{
       box[0] = Q[index] / 100;
       box[1] = Q[index] % 100 / 10;
       box[2] = Q[index] % 10;
       return getBoxState();
}
//初始化
void init()
{
       for(rint i = 0; i < SIZE; i++)
       {
             vis[i] = 0;
             Q[i] = 0;
       }
       ansCnt=0;
       step = 0;
       Q[step++] = 800;
       vis[800] = 1;
}
//核心深搜
void DFS()
{
       int state = getBoxState();
       if(state == END)//找到答案,打印出每一步状态
       {
             ansCnt++;
             printf("the %d answer, step = %d\n", ansCnt, step);
             for(rint i = 0; i < step; i++)
             {
                    printf("%d %d %d\n", Q[i]/100, Q[i]%100/10, Q[i]%10);
             }
             printf("\n");
             return;
       }
       //遍历倒水方式,得到下一步状态
       for(rint from = 0; from < 3; from++)
       {
             for(rint to = 0; to < 3; to++)
             {
                    if(isVaildAction(from, to))//是否有效的倒水
                    {
                           int nextState = getNextState(from, to);//倒水后的状态
                           Q[step] = nextState;
                           if(!vis[nextState])//是否访问过了
                           {
                                 vis[nextState] = 1;
                                 step++;
                                 DFS();                           
                                 step--;
                                 vis[nextState] = 0;
                           }
                           restoreState(step-1);//退回上一步的状态
                    }
             }
       }

}
int main()
{
    init();
    DFS();
    return 0;
}

本文原创首发于微信公众号 [ 林里少年 ],欢迎关注第一时间获取更新。

8升水等分问题_第1张图片

你可能感兴趣的:(8升水等分问题)