標籤:pop 實現 重複判斷 最大的 題目 mem 最大 return online
最近複習了一下寬搜,感覺其實搜尋是一種精神,而這種精神就是不怕困難走到底,掌握這種精髓能很好地協助您解決一些需要舉例子的問題。
今天找了一道題,比較值得學習,希望通過這道題瞭解寬搜的套路。
題目來源:http://acm.nyist.net/JudgeOnline/problem.php?pid=21(在此感謝作者!)
順便附上題目:
三個水杯
時間限制:1000 ms | 記憶體限制:65535 KB
難度:4
描述
給出三個水杯,大小不一,並且只有最大的水杯的水是裝滿的,其餘兩個為空白杯子。三個水杯之間相互倒水,並且水杯沒有標識,只能根據給出的水杯體積來計算。現在要求你寫出一個程式,使其輸出使初始狀態到達目標狀態的最少次數。
輸入
第一行一個整數N(0<N<50)表示N組測試資料
接下來每組測試資料有兩行,第一行給出三個整數V1 V2 V3 (V1>V2>V3 V1<100 V3>0)表示三個水杯的體積。
第二行給出三個整數E1 E2 E3 (體積小於等於相應水杯體積)表示我們需要的最終狀態
輸出
每行輸出相應測試資料最少的倒水次數。如果達不到目標狀態輸出-1
範例輸入
2
6 3 1
4 1 1
9 3 2
7 1 1
範例輸出
3
-1
附上AC代碼詳解,可能看起來有點繁瑣,其實並沒有你想象中那麼難,要理解,三個杯子互相倒水有6種情況!相信您能理解!
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct node{ //結構體表示倒水過程中的狀態
int a,b,c;
int step; //step表示到當前這個狀態所需要的步驟數
}b,e; //b、e分別表示開始狀態和目標狀態
int v1,v2,v3;
bool f[105][105][105]; //f(flag)表示三個杯子的這種狀態是否討論過,可以判重
//寬搜其他題比如要用hash值也是為了避免重複判斷狀態,節省時間
int bfs(node b)
{
queue<node> q; //一般寬搜都用隊列實現
q.push(b);
f[b.a][b.b][b.c]=true;
node cur;
while (!q.empty()) //6種倒水情況 :耐心,其實懂了一個其他都可以複製粘貼加一點小修改
{
cur=q.front();
if (cur.a==e.a && cur.b==e.b && cur.c==e.c) //搜尋到了就返回,也算個剪枝
return cur.step;
q.pop();
if (cur.a>0 && cur.b<v2) //v1往v2中倒水
{
int t=min(cur.a,v2-cur.b); //重點理解 兩個杯子中水的改變數倒水瓶中所有的水和被灌水瓶剩的部分中的最小值
node temp;
temp.a=cur.a-t;
temp.b=cur.b+t;
temp.c=cur.c;
temp.step=cur.step+1; //步數加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}
if (cur.a>0 && cur.c<v3) //v1往v3中倒水
{
int t=min(cur.a,v3-cur.c); //重點理解
node temp;
temp.a=cur.a-t;
temp.b=cur.b;
temp.c=cur.c+t;
temp.step=cur.step+1; //步驟數加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}
if (cur.b>0 && cur.a<v1) //v2往v1中倒水
{
int t=min(cur.b,v1-cur.a); //重點理解
node temp;
temp.a=cur.a+t;
temp.b=cur.b-t;
temp.c=cur.c;
temp.step=cur.step+1; //步驟數加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}
if (cur.b>0 && cur.c<v3) //v2往v3中倒水
{
int t=min(cur.b,v3-cur.c); //重點理解
node temp;
temp.a=cur.a;
temp.b=cur.b-t;
temp.c=cur.c+t;
temp.step=cur.step+1; //步驟數加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}
if (cur.c>0 && cur.a<v1) //v3往v1中倒水
{
int t=min(cur.c,v1-cur.a); //重點理解
node temp;
temp.a=cur.a+t;
temp.b=cur.b;
temp.c=cur.c-t;
temp.step=cur.step+1; //步驟數加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}
if (cur.c>0 && cur.b<v2) //v3往v2中倒水
{
int t=min(cur.c,v2-cur.b); //重點理解
node temp;
temp.a=cur.a;
temp.b=cur.b+t;
temp.c=cur.c-t;
temp.step=cur.step+1; //步驟數加1
if (!f[temp.a][temp.b][temp.c])
{
q.push(temp);
f[temp.a][temp.b][temp.c]=true;
}
}
}
return -1; //表示仍然找不到目標狀態
}
int main()
{
int n;
cin>>n;
while (n--) //多次詢問
{
cin>>v1>>v2>>v3;
b.a=v1; b.b=0; b.c=0; b.step=0; //初始化
cin>>e.a>>e.b>>e.c; //目標狀態
memset(f,0,sizeof(f)); //每次都要將標記清零,故放在迴圈裡
if (v1!=e.a+e.b+e.c) //因為這樣是肯定找不到的
cout<<"-1"<<endl;
else cout<<bfs(b)<<endl; //否則寬搜
}
return 0;
}
因為首次分享,定有許多不盡人意之處,還請大家諒解!
C++寬搜經典題——三個水杯(一題懂寬搜!)