HDU 1495 非常可樂(不一樣的bfs)
非常可樂Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 25387 Accepted Submission(s): 9877 Problem Description 大家一定覺的運動以後喝可樂是一件很愜意的事情,但是seeyou卻不這麼認為。因為每次當seeyou買了可樂以後,阿牛就要求和seeyou一起分享這一瓶可樂,而且一定要喝的和seeyou一樣多。但seeyou的手中只有兩個杯子,它們的容量分別是N 毫升和M 毫升 可樂的體積為S (S<101)毫升 (正好裝滿一瓶) ,它們三個之間可以相互倒可樂 (都是沒有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聰明的ACMER你們說他們能平分嗎?如果能請輸出倒可樂的最少的次數,如果不能輸出"NO"。
Input 三個整數 : S 可樂的體積 , N 和 M是兩個杯子的容量,以"0 0 0"結束。
Output 如果能平分的話請輸出最少要倒的次數,否則輸出"NO"。
Sample Input 7 4 3 4 1 3 0 0 0
Sample Output NO 3
|
思路:有一個大神寫的blog已經很好了 ,我就直接轉載一下啦
原文地址:https://blog.csdn.net/qq_34374664/article/details/51646060
思考過程:這道題我一開始按照題意以及樣例用腦子模擬了一遍(可能我模擬的方式不對誤導了我),感覺應該是用深搜,類似於全排列,就是一個杯子倒過來到過去,後來寫了寫,寫到一半寫不下去了,卡住了。看了看網上的程式碼,普遍很長,而且用的是廣搜,我一直不理解為什麼可以用廣搜,他們解釋的是一共就有6個狀態a1-a2,a2-a1,a1-a3,a3-a1,a2-a3,a3-a2,這六個狀態每次都做一邊就好了。。我當時是懵逼的。。我以為他就是按照這個順序,每倒一次就step+1,那樣肯定不對啊。。後來我就照著網上的程式碼寫(不理解題意但是程式碼知道什麼意思),做了一些優化,用兩個for迴圈做控制倒水。那天問了問斌神,他跟我說了說,我豁然開朗,原來這6個狀態算“一步”,不是倒一次水算一步。而且最少最短這種等權問題一般都用廣搜。。
總結:1,深刻理解廣搜,以前總是侷限於地圖,迷宮的問題,這題也可以用廣搜,以後求最短,最快,最少的問題(等權)都可以往廣搜上面想想。。。理解每個”step“的含義,這裡每一個step就是在正確的倒水路徑上的每一次倒水,一共有6次倒水的方法,讓每個“step”做這6個“動作”,然後一直這樣往下”各個方向瀰漫“,就一定會找到答案。
2.以前總是做一些地圖迷宮的問題,思想拘束在”走一步這個動作“就是一個step,把思維開啟,一個step可以有”n個動作“(一定是所有可能性都包含),而且這n個動作是平行的。這也是這題的關鍵。
3.自己還是刷題太少了,接觸的太少了,思維太侷限,而且還有一些輸出的小細節自己可能也注意不到,感謝”斌大腿“的指教與點播,發現自己與他的差距還是很大。。
#include <queue>
#include <cstring> //用來給陣列賦初值為0的,memset;
using namespace std;
int b[3],book[101][101][101],half;
struct node
{
int cur[3],s; //cur表示現在瓶子裡的水,b表示水的容量
}p,temp; //p獲取當前的“head”,temp用於6個動作的記錄。
void bfs()
{
queue<node> q; //宣告一個佇列,由於經常做pat乙 只有一組資料習慣宣告全域性變數,如果把這句放在bfs()外邊,因為有多組
p.cur[0]=b[0]; //測試資料,如果不把之前佇列裡的資料清空就會帶到下一組測試中(做題考慮下這個機制會不會對某些題目有用)
p.cur[1]=p.cur[2]=p.s=0; //放到bfs裡面 每次佇列都是新的。
q.push(p); //將最開始狀態初始化 加入佇列q中;
while(!q.empty())
{
p=q.front();
q.pop();
for(int i=0;i<3;i++) //控制哪個杯子往外倒水
{
if(p.cur[i]>0) //如果有水才可以給別的倒水。
{
for(int j=0;j<3;j++) //接受水的杯子
{
temp=p; //這很重要!之前沒加這句一直wa,因為這6個動作是”平行“的,即都是從前一步狀態開始的,如果沒有
if(i==j) continue ; //這一句,就不是平行的,相互疊加的。這個很重要!
if(temp.cur[i]>b[j]-temp.cur[j]) //判斷能不能倒滿了,這是可以倒滿的情況
{
temp.cur[i]-=b[j]-temp.cur[j]; //這兩句話一定不要顛倒,之前因為這個一直不出答案,先運算後賦值。。。
temp.cur[j]=b[j];
}
else // 不可以倒滿的情況
{
temp.cur[j]+=temp.cur[i];
temp.cur[i]=0;
}
if(!book[temp.cur[0]][temp.cur[1]][temp.cur[2]]) //判斷這種情況是否出現過
{
book[temp.cur[0]][temp.cur[1]][temp.cur[2]]=1; //標記為出現過,說明這一步“有效”,step+1;
temp.s++;
if((temp.cur[0]==half&&temp.cur[1]==half)||(temp.cur[0]==half&&temp.cur[2]==half)||(temp.cur[1]==half&&temp.cur[2]==half))
{
cout << temp.s << endl; // step裡的每一個“動作”都要判斷是否符合條件,因為這動作是平行的 所以放在內迴圈裡面!
return ; //直接跳出bfs
}
q.push(temp);// 如果不是所求,就把他加到佇列裡。之後在從每一個“動作呼叫”
}
}
}
}
}
cout <<"NO"<<endl; //如果整個迴圈結束還是沒有找到說明不可以平分;
}
int main()
{
while(cin >> b[0]>>b[1]>>b[2],b[0]+b[1]+b[2]) //如果相加都等於0 即000 用於結束
{
memset(book,0,sizeof(book)); //多組資料每一組都要賦初值,想用book【】【】={0},必須要在宣告book陣列時候用;
book[b[0]][b[1]][b[2]]=1; //把初始點標記來過,這一點在呼叫bfs之前做 很多題都要注意這一點。
if(b[0]%2) cout << "NO"<<endl;
else {half=b[0]/2;bfs();}
}
return 0;
}