程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> yzoi1777倒水問題的詳細解法,yzoi1777倒水解法

yzoi1777倒水問題的詳細解法,yzoi1777倒水解法

編輯:C++入門知識

yzoi1777倒水問題的詳細解法,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。

這是我的第一篇博文,歡迎各位大神的指點。

 

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved