多維建圖 1
例題:
題目描述
迷宮用 n*mn∗m 的網格表示,分為以下四種格子:
X:牆,不能通過
.:空地,可以通過
^:陷阱(開啟):可以通過,但行動後如果處在開啟的陷阱上上會額外消耗1點體力
_:陷阱(關閉):可以通過
每次你可以從當前格子花費1點體力進行一次行動,包括移動到上下左右四方向的某一個可以通過的格子,或者停留在原地。
在每次行動時,所有陷阱的狀態會在開啟和關閉之間切換。如果相鄰格子當前為一開啟的陷阱,當你移動到上面時它會關閉,所以不會額外消耗體力。反之如果相鄰一個當前關閉的陷阱,則移動到上面需要額外消耗1體力。
你的起點為迷宮左上角,終點為迷宮右下角,你需要找到一個消耗體力值最少的行動方法。
保證起點和終點處為空地。
輸入格式
第一行兩個整數n, mn,m,分別表示迷宮的行數和列數
接下來nn行mm列,每個字元代表一個格子,見題目描述,表示開始時迷宮的狀態
輸出格式
一行一個整數,表示最少消耗的體力
輸入輸出樣例
輸入 #1
5 5
.....
^XXX.
_X...
.X.XX
^....
輸出 #1
9
顯然,走的步數是奇數步時,地圖狀態都一樣;走了偶數步時,地圖狀態也都一樣。在沒有陷阱的情況下,我們可以通過建一個兩層的二維圖,第一層為偶數層(因為從0步開始,0為偶數),第二層為奇數層。每走一步,就去到另一個層的對應點,如圖:
當有陷阱時,我們有兩種決策,第一種是做一次停留,另一種是踩進陷阱並因此額外耗費一點體力。
當停留時,位置沒有改變,但是因為停留也是一種行動,步數的奇偶性仍會發生改變,因此,在二維圖中,所處的維度會去到另一層,如圖:
當踩入陷阱時,會額外耗費一點體力,相當於做了一次行動卻耗費了兩點體力,那麼,我們可以通過建立一個第三維度,當在所處維度踩到陷阱時,再耗費一點體力去到第三維度的對應位置,然後從第三維度的對應位置再耗費一點體力去到下一個對應的維度。因為在同一個陷阱上停留是沒有意義的,所以不考慮第三維度的停留問題。
當在偶數維度踩到陷阱時,建立一條從當前的點到第三維度對應點的邊,再建立一條從第三維度對應點到奇數維度下一個點的邊,用來等效額外的體力消耗。奇數維度時如法炮製。
如圖:
程式碼實現:
#include<cstring> #include<vector> #include<queue> #include<iostream> using namespace std; const int maxn=510; int n,m,pow[maxn*maxn*3],dx[4]={-1,1,0,0},dy[4]={0,0,-1,1},ed0,ed1,ans; vector <int> e[maxn*maxn*3]; queue <int> q; char str[maxn][maxn]; void ist(int u,int v) { e[u].push_back(v); } void bfs() { q.push(0); while(!q.empty()) { int t=q.front(); q.pop(); if(t==ed0 || t==ed1) return;//只要第一次到達了終點,那麼此時體力耗費一定最小 for(int i=0;i<e[t].size();i++) { int to=e[t][i]; if(pow[to]<0) { pow[to]=pow[t]+1; q.push(to); } } } } int main() { scanf("%d %d",&n,&m); for(int i=0;i<n;i++) { scanf("%s",str[i]); } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { int s0=(i*m+j)*3,s1=s0+1;//將三維座標壓縮成一維座標 if(str[i][j]=='X') continue; ist(s0,s1),ist(s1,s0);//在s0和s1之間建一條雙向邊,因為在任意狀態都可以停留 if(str[i][j]=='^') { ist(s0,s0+2); //在走了偶數步時踩到陷阱,還需要在在偶數維度和第三維度之間建一條單向邊,等效替代踩到陷阱時多消耗的體力 //在同一個陷阱上消耗兩次體力是沒有意義的,所以只建單向邊 s0+=2;//去到第三維度 } if(str[i][j]=='_') { ist(s1,s1+1);//走了奇數步踩到陷阱,在奇數維度和第三維度之間建一條邊 s1++;//去到第三維度 } for(int k=0;k<4;k++) { int mx=i+dx[k],my=j+dy[k]; if(mx<0 || mx>=n || my<0 || my>=m || str[mx][my]=='X') continue; int t0=(mx*m+my)*3,t1=t0+1; ist(s0,t1),ist(s1,t0);//當前是奇維度,下一步就走向偶維度;反之亦然 } } } ed0=((n-1)*m+m-1)*3,ed1=ed0+1;//終點的一維座標 memset(pow,-1,sizeof(pow)); pow[0]=0; bfs(); if(pow[ed0]>0 && pow[ed1]>0) ans=min(pow[ed0],pow[ed1]); else if(pow[ed0]>0) ans=pow[ed0]; else ans=pow[ed1]; printf("%d\n",ans); return 0; }