Description - 問題描述
輸入只有一行,即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。
這是我的第一篇博文,歡迎各位大神的指點。