zoukankan      html  css  js  c++  java
  • yzoi1777倒水问题的详细解法

    Description - 问题描述

    x、y、z三个容器,其最大容量分别是xMAX升、yMAX升、zMAX升,这里规定100>xMAX>yMAX>zMAX。一开始x是装满了水的,现在要用这三个没有刻度的容器量出n升水来,请打印出最少的量取步骤。
    Input - 输入数据

    输入只有一行,即4个整数:

    xMax  yMax  zMax n

    其中100>xMax>yMax>zMax,n<100

    Output - 输出数据

    对于每个测试数据,输出其倒水步骤,如果步骤只有一步,step不加s请参考Sample Output,无解的话输出“No Solution”

    算法分析

      首先,我们需要考虑倒出者和接收者的可能分别为x->y,x->z(前提是x>0);y->x,y->z(前提是y>0);z->x,z->y(前提是z>0)共6种可能,接下来我们便对其进行详细分析。

    ①x->y时

    前提:if(x>0),接着,我们要考虑的就是当将x中所有水倒入y中,是否已经超过了y的容量,代码如下

     1           if(x>0)
     2           {
     3               if(x+y>ymax)
     4               {
     5                   x=x-(ymax-y);//将x中所有水倒入y中时已超出y的最大容量
     6                   y=ymax;
     7               }
     8               else
     9               {
    10                  y=x+y;//将x中所有水倒入y中后未超过其最大容量
    11                 x=0;//即x中的水全部倒入y中,x变为空
    12              }
    13          }

    ②x->z时

    前提:if(x>0)这时,我们可以将x->z的过程类比成x->y的过程,其实际上的过程是一样的,代码如下:

     1         if(x>0)
     2         {
     3             if(x+z>zmax)
     4             {
     5                 x=x-(zmax-z);//z容器倒满
     6                 z=zmax;
     7             }
     8             else
     9             {
    10                 z=z+x;//x倒空
    11                 x=0;
    12             }
    13         }

    ③y->x时

    前提:if(y>0),这是,我们还要不要像以前那样考虑y会不会倒空,已及x会不会倒满呢?其答案是否定的,为什么呢?你可以设想:在整个装置(包括x,y以及z)中,其总水量之和为xmax。故不管y如何倒都不会使x>xmax,代码如下:

    1         if(y>0)
    2         {
    3             x=x+y;//y倒空
    4             y=0;
    5         }

    ④y->z时,同x->y

    ⑤z->x时,同y->x

    ⑥z->y,这里也是同y->x吗?其实不然,当y->x时,无论如何都不会使x>xmax。但当z->y时却有可能使y>ymax,很多人都跳入了这个坑里,导致最终程序不能ac。经过仔细思考之后,其实可以知道这是和x->y一样的。代码如下:

     1             if(z>0)
     2         {
     3             if(z+y>ymax)
     4             {
     5                 z=z-(ymax-y);
     6                 y=ymax;
     7             }
     8             else
     9             {
    10                 y=y+z;
    11                 z=0;
    12             }
    13         }    

      在对倒水的过程进行详细分析之后,最重要的便是算法的构造。仔细分析可以知道,这道题是要求我们分析倒水的每种情况,然后找出最优解。毫无疑问,这便是bfs的思路。可能有些人不了解或对bfs接触较少,这里给上百度对其的解释以助理解:

    BFS一般指宽度优先搜索,宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。而在这个问题中,我们使用队列(queue)来实现之。

      首先,假设我们已经定义了一个队列q,如下所示

    struct node
    {
        int qx,qy,qz;
        int pre;
    } q[maxn];

    其中qx,qy,qz表示当前各个装置中水的含量,而对于pre,相信大多数人都会纳闷:这是用来干什么的呢?其实,当你仔细阅读题目便可知道,它不仅要求输出最少次数,还得打印出每倒一次水的步骤,这里的pre便是用以存储当前状态所对应的上一个状态,到最后输出时,只需沿着pre进行输出即可。

    每倒一次水,就想当于产生了一个新的状态,于是就要判断这个状态是否已达到目标。因此每倒一次水,就要调用一次push过程,将新状态入队,然后判断队尾元素是否已是解,push的代码如下:
     1 void push()
     2 {
     3     bool can_push=true;
     4     for(int i=1;i<=rear;i++)
     5     {
     6         if(q[i].qx==x&&q[i].qy==y&&q[i].qz==z)
     7         {
     8             can_push=false;
     9             return;
    10         }
    11     }
    12     if(can_push)
    13     {
    14         rear++;
    15         q[rear].qx=x;
    16         q[rear].qy=y;
    17         q[rear].qz=z;
    18         q[rear].pre=front;
    19     }
    20 } 

    同时,在倒水时如果先前已经出现了与当前状态一致的状态,那还要不要继续倒呢?显然,答案是否定的,而这里的bool型变量can_push正是帮助完成了这一点.

    在倒完水后,我们还需判断是否达到了目标状态,如果达到了就跳出循环,然后输出过程,代码如下:

    void is_target()
    {
        if(q[rear].qx==n||q[rear].qy==n||q[rear].qz==n)
            flag=true;
        else
            flag=false;
    }

    细心的人可能会发现:在模拟倒水过程时的x,y,z三个变量是哪来的呢?以是,我们还得写个取队首元素的函数,代码如下;

    void initial()
    {
        x=q[front].qx;
        y=q[front].qy;
        z=q[front].qz;
        
    }

    最后,我们再将刚刚模拟过的6种情况写下,再加一个while循环便可完成任务。接下来还有一点值得思考:这里的while循环的条件是什么。还记得题目中给出的“No Solution”吗。是的,在最后跳出循环后我们还得加一条is_target()(其实在跳出循环之前已经执行了一次is_target(),故只需判断flag即可)以判断是否有解。经过这样的分析后,while执行的条件便是队列是否为空is_empty()。倒水总过程的代码如下:

      1 void pour()
      2 {
      3     while(front <= rear)
      4     {
      5         initial();
      6         //x->y
      7         if(x>0)
      8         {
      9             if(x+y>ymax)
     10             {
     11                 x=x-(ymax-y);
     12                 y=ymax;
     13             }
     14             else
     15             {
     16                 y=x+y;
     17                 x=0;
     18             }
     19         }
     20         push();
     21         is_target();
     22         if(flag)
     23             break;
     24         //x->z
     25         initial();
     26         if(x>0)
     27         {
     28             if(x+z>zmax)
     29             {
     30                 x=x-(zmax-z);
     31                 z=zmax;
     32             }
     33             else
     34             {
     35                 z=z+x;
     36                 x=0;
     37             }
     38         }
     39         push();
     40         is_target();
     41         if(flag)
     42             break;
     43         //y->x
     44         initial();
     45         if(y>0)
     46         {
     47             x=x+y;
     48             y=0;
     49         }
     50         push();
     51         is_target();
     52         if(flag)
     53             break;
     54         //y->z
     55         initial();
     56         if(y>0)
     57         {
     58             if(y+z>zmax)
     59             {
     60                 y=y-(zmax-z);
     61                 z=zmax;
     62             }
     63             else
     64             {
     65                 z=y+z;
     66                 y=0;
     67             }
     68         }
     69         push();
     70         is_target();
     71         if(flag)
     72             break;
     73         //z->x
     74         initial();
     75         if(z>0)
     76         {
     77             x=x+z;
     78             z=0;
     79         }
     80         push();
     81         is_target();
     82         if(flag)
     83             break;
     84         //z->y
     85         initial();
     86         if(z>0)
     87         {
     88             if(z+y>ymax)
     89             {
     90                 z=z-(ymax-y);
     91                 y=ymax;
     92             }
     93             else
     94             {
     95                 y=y+z;
     96                 z=0;
     97             }
     98         }
     99         push();
    100         is_target();
    101         if(flag)
    102             break;        
    103         front++;                    
    104     }
    105 }

    还有一点要补充的就是这题也可以用STL里的queue来做,这样便可省去自己写队列的麻烦。

    最后,给上OJ上ac的代码,仅供参考:

      1 /*
      2     Name: lwq
      3     Copyright: 
      4     Author: 
      5     Date: 07/12/14 00:57
      6     Description: 1777
      7 */
      8 
      9 #include<iostream>
     10 using namespace std;
     11 const int maxn=10000+10;
     12 int front,rear; 
     13 int xmax,ymax,zmax;
     14 int n;
     15 int x,y,z;
     16 int step=0;
     17 int k=0;
     18 bool flag=false;
     19 struct node
     20 {
     21     int qx,qy,qz;
     22     int pre;
     23 } q[maxn];
     24 struct node_data
     25 {
     26     int x,y,z;
     27 }data[maxn];
     28 void is_target()
     29 {
     30     if(q[rear].qx==n||q[rear].qy==n||q[rear].qz==n)
     31         flag=true;
     32     else
     33         flag=false;
     34 }
     35 void initial()
     36 {
     37     x=q[front].qx;
     38     y=q[front].qy;
     39     z=q[front].qz;
     40     
     41 }
     42 void push()
     43 {
     44     bool can_push=true;
     45     for(int i=1;i<=rear;i++)
     46     {
     47         if(q[i].qx==x&&q[i].qy==y&&q[i].qz==z)
     48         {
     49             can_push=false;
     50             return;
     51         }
     52     }
     53     if(can_push)
     54     {
     55         rear++;
     56         q[rear].qx=x;
     57         q[rear].qy=y;
     58         q[rear].qz=z;
     59         q[rear].pre=front;
     60     }
     61 } 
     62 void pour()
     63 {
     64     while(front <= rear)
     65     {
     66         initial();
     67         //x->y
     68         if(x>0)
     69         {
     70             if(x+y>ymax)
     71             {
     72                 x=x-(ymax-y);
     73                 y=ymax;
     74             }
     75             else
     76             {
     77                 y=x+y;
     78                 x=0;
     79             }
     80         }
     81         push();
     82         is_target();
     83         if(flag)
     84             break;
     85         //x->z
     86         initial();
     87         if(x>0)
     88         {
     89             if(x+z>zmax)
     90             {
     91                 x=x-(zmax-z);
     92                 z=zmax;
     93             }
     94             else
     95             {
     96                 z=z+x;
     97                 x=0;
     98             }
     99         }
    100         push();
    101         is_target();
    102         if(flag)
    103             break;
    104         //y->x
    105         initial();
    106         if(y>0)
    107         {
    108             x=x+y;
    109             y=0;
    110         }
    111         push();
    112         is_target();
    113         if(flag)
    114             break;
    115         //y->z
    116         initial();
    117         if(y>0)
    118         {
    119             if(y+z>zmax)
    120             {
    121                 y=y-(zmax-z);
    122                 z=zmax;
    123             }
    124             else
    125             {
    126                 z=y+z;
    127                 y=0;
    128             }
    129         }
    130         push();
    131         is_target();
    132         if(flag)
    133             break;
    134         //z->x
    135         initial();
    136         if(z>0)
    137         {
    138             x=x+z;
    139             z=0;
    140         }
    141         push();
    142         is_target();
    143         if(flag)
    144             break;
    145         //z->y
    146         initial();
    147         if(z>0)
    148         {
    149             if(z+y>ymax)
    150             {
    151                 z=z-(ymax-y);
    152                 y=ymax;
    153             }
    154             else
    155             {
    156                 y=y+z;
    157                 z=0;
    158             }
    159         }
    160         push();
    161         is_target();
    162         if(flag)
    163             break;        
    164         front++;                    
    165     }
    166 }
    167 int main()
    168 {
    169     front=1,rear=1;
    170     cin>>xmax>>ymax>>zmax;
    171     cin>>n;
    172     q[front].qx=xmax;
    173     q[front].qy=0;
    174     q[front].qz=0;
    175     pour();
    176     
    177     
    178     if(flag)
    179     {
    180     
    181     
    182     while(q[rear].pre>0)
    183     {
    184         k++;
    185         data[k].x=q[rear].qx;
    186         data[k].y=q[rear].qy;
    187         data[k].z=q[rear].qz;
    188         rear=q[rear].pre;
    189     }
    190     if(k>1)
    191         cout<<k<<' '<<"steps"<<endl;
    192     else
    193         cout<<k<<' '<<"step"<<endl;
    194     cout<<"step0:"<<' '<<xmax<<' '<<'0'<<' '<<'0'<<endl;
    195     for(int i=k;i>=1;i--)
    196     {
    197         cout<<"step"<<k-i+1<<':'<<' '<<data[i].x<<' '<<data[i].y<<' '<<data[i].z<<endl; 
    198     }
    199     }
    200     else
    201     {
    202         cout<<"No Solution"<<endl;
    203     }
    204     return 0;
    205 }

    最后,还要感谢YYL大神的不啬指教以及xaero的ppt。

    这是我的第一篇博文,欢迎各位大神的指点。

  • 相关阅读:
    数据库里面的表空间,用户在实际开发中的使用
    并行编程的模型机制
    临时表在开发中的使用

    HIbernate编程模型
    网络数据如何可靠传输
    Spring Security编程模型
    java的缓存框架
    sort函数使用自定义数据排序使用
    FailOver的机制
  • 原文地址:https://www.cnblogs.com/luowenqing/p/4149373.html
Copyright © 2011-2022 走看看