【NOJ1044】【分支限界法】獨輪車
阿新 • • 發佈:2018-12-14
1044.獨輪車
時限:1000ms 記憶體限制:10000K 總時限:3000ms
描述
獨輪車的輪子上有紅、黃、藍、白、綠(依順時針序)5種顏色
在一個如下圖所示的20*20的迷宮內每走一個格子,輪子上的顏色變化一次(原地轉向則不變色)
獨輪車只能向前推或在原地轉向。每走一格或原地轉向90度均消耗一個單位時間。
現給定一個起點(S)和一個終點(T),求獨輪車以輪子上的指定顏色(未指定車頭朝向)到達終點所需的最短時間。
輸入
本題包含一個測例。
測例中分別用一個大寫字母表示方向和輪子的顏色,其對應關係為:E-東、S-南、W-西、N-北;R-紅、Y-黃、B-藍、W-白、G-綠。
在測試資料的第一行有以空格分隔的兩個整數和兩個
第二行有以空格分隔的兩個整數和一個大寫字母,表示終點的座標T(x,y)和到達終點時輪子的顏色(不指定結束時方向),
從第三行開始的20行每行內包含20個字元,表示迷宮的狀態。其中'X'表示建築物,'.'表示路.
輸出
在單獨的一行內輸出一個整數,即滿足題目要求的最短時間。
#include <iostream> #include <queue> using namespace std; /******************全域性變數***************************/ char maze[21][21]; //存放迷宮 struct node //記錄獨輪車的狀態 { int x; //第x行,從1開始 int y; //第y列,從1開始 int color; //車輪顏色,0==紅,1==黃,2==藍,3==白,4==旅 int dire; //車頭朝向,0==西,1==南,2==東,3==北 }; node start,target; //起始狀態,末狀態 queue <node> state; //廣搜所用佇列 int used[21][21][5][4]; //此狀態是否到達過 int time[21][21][5][4]; //到達此狀態所用的時間 /******************全域性變數***************************/ /******************函式宣告***************************/ void input(); //輸入資料所用函式 node inputState(int x, int y, char c, char d); //輸入初始狀態/末狀態函式 bool bfs(); //廣搜,成功返回true bool canmoveto(node n1); //判斷n1狀態能否向前走一格 node moveto(node n1); //返回n1狀態向前走一格後達到的狀態(同時更新used陣列和time陣列) bool canturnto(node n1, int i); //判斷n1狀態能否向i方向原地轉向,i=0表示逆時針九十度,i=1順時針九十度 node turnto(node n1, int i); //返回n1狀態向i方向轉向後到達的狀態(同時更新used陣列和time陣列) bool ftarget(node n1); //判斷n1狀態是不是目標狀態 /******************函式宣告***************************/ int main() { input(); //輸入資料 if(bfs()) //廣搜 { //輸出從初始狀態到目標狀態所用時間,因為目標狀態只指定了座標和顏色, //與“車頭朝向”無關,因此設車頭朝向為西,即target.dire=0 cout<<time[target.x][target.y][target.color][0]<<endl; } else { cout<<"error"<<endl; } return 0; } /***********以下均為函式實現***************************/ void input() //輸入資料 { int x,y; char c,d; //輸入初始狀態 cin>>x>>y>>c>>d; start=inputState(x,y,c,d); //輸入目標狀態 cin>>x>>y>>c; target=inputState(x,y,c,'W'); //末狀態的dire不重要,因此設為西 //輸入地圖 for(int i=1; i<21; i++) { for(int j=1; j<21; j++) { cin>>maze[i][j]; } } } node inputState(int x, int y, char c, char d) //輸入一個狀態 { node n1; n1.x=x; n1.y=y; switch(c) //輸入顏色,將字元轉化為數字 { case 'R':n1.color=0;break; case 'Y':n1.color=1;break; case 'B':n1.color=2;break; case 'W':n1.color=3;break; case 'G':n1.color=4;break; } switch(d) //輸入車頭朝向,將字元轉化為數字 { case 'W':n1.dire=0;break; case 'S':n1.dire=1;break; case 'E':n1.dire=2;break; case 'N':n1.dire=3;break; } return n1; } bool bfs() //核心演算法,廣搜 { //將初始狀態入隊 state.push(start); //更新used陣列:標記此狀態已到達 used[start.x][start.y][start.color][start.dire]=1; //更新time陣列:起點到起點所用時間為0 time[start.x][start.y][start.color][start.dire]=0; //開始搜尋 node top; while(!state.empty()) { top=state.front(); //取隊首元素 state.pop(); //嘗試向前走一格 if(canmoveto(top)) //如果top狀態能夠往前走一格 { node next=moveto(top); //獲得前進一格之後的狀態next state.push(next); //將next狀態入隊 if(ftarget(next)) //如果next狀態就是target狀態,退出搜尋 { return true; } } //else{cout<<"不能走"<<top.x<<' '<<top.y<<' '<<top.dire<<endl;} //嘗試原地轉向 for(int i=0; i<2; i++) //i=0表示逆時針轉九十度,i=1表示順時針轉九十度 { if(canturnto(top, i)) //如果top狀態能夠向i方向轉九十度 { node next=turnto(top, i); //獲得轉向之後的狀態next state.push(next); //將next狀態入隊 if(ftarget(next)) //如果next狀態就是target狀態,退出搜尋 { return true; } } } } return false; //如果佇列為空,仍未搜到目標狀態,返回false } //判斷身處狀態n1,能否向“前”走一格,“前”就是車頭朝向 //不可走條件:越界、前面是牆、狀態重複 bool canmoveto(node n1) { switch(n1.dire) //根據車頭朝向,判斷應朝哪個方向走一格 { case 0: //W 西(左) { if(n1.y-1<1 //越界 ||maze[n1.x][n1.y-1]=='X' //或前方是牆 ||used[n1.x][n1.y-1][(n1.color+1)%5][n1.dire]==1) //或狀態重複 { return false; } break; } case 1: //N 南(下) { if(n1.x+1>20 //越界 ||maze[n1.x+1][n1.y]=='X' //或前方是牆 ||used[n1.x+1][n1.y][(n1.color+1)%5][n1.dire]==1) //或狀態重複 { return false; } break; } case 2: //E 東(右) { if(n1.y+1>20 //越界 ||maze[n1.x][n1.y+1]=='X' //或前方是牆 ||used[n1.x][n1.y+1][(n1.color+1)%5][n1.dire]==1) //或狀態重複 { return false; } break; } case 3: //B 北(上) { if(n1.x-1<1 //越界 ||maze[n1.x-1][n1.y]=='X' //或前方是牆 ||used[n1.x-1][n1.y][(n1.color+1)%5][n1.dire]==1) //或狀態重複 { return false; } break; } } return true; } //返回身處n1,向前走一格後達到的狀態n2(並更新used陣列和time陣列) node moveto(node n1) { node n2; n2.dire=n1.dire; //向前走一格後,車頭朝向依然不變 n2.color=(n1.color+1)%5; //顏色向前輪轉一個,防止向上溢位 switch(n1.dire) //根據車頭朝向,判斷前進一格後的橫縱座標 { case 0: //西(左) { n2.x=n1.x; n2.y=n1.y-1; //面向西走一格 break; } case 1: //南(下) { n2.x=n1.x+1; n2.y=n1.y; break; } case 2: //東(右) { n2.x=n1.x; n2.y=n1.y+1; break; } case 3: //北(上) { n2.x=n1.x-1; n2.y=n1.y; break; } } //更新used陣列,標記n2狀態已到達過 used[n2.x][n2.y][n2.color][n2.dire]=1; //更新time陣列,到達n2狀態,比到達n1狀態多用了一個時間 time[n2.x][n2.y][n2.color][n2.dire] = 1 + time[n1.x][n1.y][n1.color][n1.dire]; return n2; } //判斷能否向i方向原地轉向,0==逆時針九十度,1==順時針九十度 //不可轉條件:狀態重複 bool canturnto(node n1, int i) { if(i==0) //逆時針轉九十度 { if(used[n1.x][n1.y][n1.color][(n1.dire+1)%4]==1) { return false; } } else { if(used[n1.x][n1.y][n1.color][(n1.dire+4-1)%4]==1) { return false; } } return true; } //返回身處n1,向i方向轉九十度後到達的狀態n2(並更新used陣列和time陣列) node turnto(node n1, int i) { node n2; n2.x=n1.x; //原地轉向,位置不變 n2.y=n1.y; n2.color=n1.color; //原地轉向,顏色不變 if(i==0) //逆時針轉九十度 { n2.dire=(n1.dire+1)%4; //更新車頭朝向,防止向上溢位 } else //順時針轉九十度 { n2.dire=(n1.dire+4-1)%4; //更新車頭朝向,防止向下溢位 } //更新used陣列,標記n2狀態已到達 used[n2.x][n2.y][n2.color][n2.dire]=1; //更新time陣列,從起點到達n2狀態,比從起點到n1狀態,多用了一個時間 time[n2.x][n2.y][n2.color][n2.dire] = 1 + time[n1.x][n1.y][n1.color][n1.dire]; return n2; } //判斷n1是否到達目標狀態 bool ftarget(node n1) { //目標狀態的dire不重要,故不加入判斷條件 if(n1.x==target.x&&n1.y==target.y&&n1.color==target.color) //若到達目標狀態 { //將從起點到達狀態n1(dire=任意)的時間,賦值給我們指定的目標狀態target(dire=0) time[target.x][target.y][target.color][0]=time[n1.x][n1.y][n1.color][n1.dire]; return true; } else { return false; } }
【後記】
1.第一次感覺程式碼寫的心力交瘁……累skr人……大概也不會有人看這麼又臭又長的程式碼叭……