棋盤(BFS)
阿新 • • 發佈:2020-07-15
原題:
1956: 棋盤(chess)
時間限制:1Sec記憶體限制:256 MB題目描述
有一個m × m的棋盤,棋盤上每一個格子可能是紅色、黃色或沒有任何顏色的。你現在要從棋盤的最左上角走到棋盤的最右下角。 任何一個時刻,你所站在的位置必須是有顏色的(不能是無色的), 你只能向上、 下、左、 右四個方向前進。當你從一個格子走向另一個格子時,如果兩個格子的顏色相同,那你不需要花費金幣;如果不同,則你需要花費 1 個金幣。 另外, 你可以花費 2 個金幣施展魔法讓下一個無色格子暫時變為你指定的顏色。但這個魔法不能連續使用, 而且這個魔法的持續時間很短,也就是說,如果你使用了這個魔法,走到了這個暫時有顏色的格子上,你就不能繼續使用魔法; 只有當你離開這個位置,走到一個本來就有顏色的格子上的時候,你才能繼續使用這個魔法,而當你離開了這個位置(施展魔法使得變為有顏色的格子)時,這個格子恢復為無色。 現在你要從棋盤的最左上角,走到棋盤的最右下角,求花費的最少金幣是多少?輸入
資料的第一行包含兩個正整數 m, n,以一個空格分開,分別代表棋盤的大小,棋盤上有顏色的格子的數量。 接下來的 n 行,每行三個正整數 x, y, c, 分別表示座標為( x, y)的格子有顏色 c。 其中 c=1 代表黃色, c=0 代表紅色。 相鄰兩個數之間用一個空格隔開。 棋盤左上角的座標為( 1, 1),右下角的座標為( m, m)。 棋盤上其餘的格子都是無色。保證棋盤的左上角,也就是( 1, 1) 一定是有顏色的。 輸入輸出樣例 1 說明 從( 1, 1)開始,走到( 1, 2)不花費金幣 從( 1, 2)向下走到( 2, 2)花費 1 枚金幣 從( 2, 2)施展魔法,將( 2, 3)變為黃色,花費 2 枚金幣 從( 2, 2)走到( 2, 3)不花費金幣 從( 2, 3)走到( 3, 3)不花費金幣 從( 3, 3)走到( 3, 4)花費 1 枚金幣 從( 3, 4)走到( 4, 4)花費 1 枚金幣 從( 4, 4)施展魔法,將( 4, 5)變為黃色,花費 2 枚金幣, 從( 4, 4)走到( 4, 5)不花費金幣 從( 4, 5)走到( 5, 5)花費 1 枚金幣 共花費 8 枚金幣。輸出
輸出一行,一個整數,表示花費的金幣的最小值,如果無法到達,輸出-1。 樣例2輸入 5 5 1 1 0 1 2 0 2 2 1 3 3 1 5 5 0 樣例2輸出: -1 輸入輸出樣例 2 說明 從( 1, 1)走到( 1, 2),不花費金幣 從( 1, 2)走到( 2, 2),花費 1 金幣 施展魔法將( 2, 3)變為黃色,並從( 2, 2)走到( 2, 3)花費 2 金幣 從( 2, 3)走到( 3, 3)不花費金幣 從( 3, 3)只能施展魔法到達( 3, 2),( 2, 3),( 3, 4),( 4, 3) 而從以上四點均無法到達( 5, 5),故無法到達終點,輸出-1樣例輸入Copy
5 7
1 1 0
1 2 0
2 2 1
3 3 1
3 4 0
4 4 1
5 5 0
樣例輸出Copy
8
資料範圍
對於 30%的資料, 1 ≤ m ≤ 5, 1 ≤ n ≤ 10。 對於 60%的資料, 1 ≤ m ≤ 20, 1 ≤ n ≤ 200。 對於 100%的資料, 1 ≤ m ≤ 100, 1 ≤ n ≤ 1,000。~~本蒟蒻第一次寫題解,寫的不好多多包涵~~
這是一道很明顯的搜尋題,最近幾天在練BFS的題目,所以講講BFS解題思路,大佬勿噴
題意:
在一個m*m的棋盤中,從(1,1)出發到(m,m),上下左右移動,移動到同色格子不需要代價,移動到不同色格子需要代價為1,不能移動到無色格子但可以花費代價2將無色格子變為有色格子,**不能從無色格子移動到另一無色格子**
首先考慮從一個格子走到另一個格子有哪些情況
(方便起見將紅色設為1,黃色設為2,無色設為0):
1. 從有色格子走到同一有色格子(1->1或2->2)
2. 從有色格子走到不同色的有色格子(1->2或2->1)
3. 從有色格子走到無色格子(此時需要使用魔法)(1->0或2->0)
4. 從施了魔法的無色格子移動到有色格子(還需考慮魔法將無色格子變成了什麼顏色)(0->1或0->2)
接著考慮搜尋到一個可走的點時需要將哪些元素壓入佇列,用一個結構體捆綁
1 struct node 2 { 3 int x,y,c,s,p; 4 }now;
其中x,y表示加入佇列的點的座標(x,y),c記錄該點原本的顏色,s記錄該點施了魔法後的顏色(若該點原本有色則s仍為原本的顏色),p記錄目前所需的代價。
因為本題需要的是代價最小而不是步數,所以最先搜尋到的點不一定是代價最小的點,再建一個v陣列統計所有可能的代價。
套入BFS的模板,程式碼如下:
1 #include<iostream> 2 #include<stdio.h> 3 #include<queue> 4 using namespace std; 5 int n,m,ans,tot,mark[1005][1005],map[1005][1005],v[5005]; 6 //tot統計所有可能的代價 7 //mark記錄到各個點的最小代價 8 //map記錄棋盤初始狀況 9 //v記錄到達終點的所有可能的代價 10 int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0}; 11 //用dx,dy兩個陣列控制移動方向 12 struct node 13 { 14 int x,y,c,s,p; 15 }now; 16 queue<node> q;//STL庫自帶的佇列 17 //聽說速度會比手打佇列慢一點,但應該關係不大 18 void bfs(int x,int y) 19 { 20 mark[x][y]=0;//起點不需要代價 21 q.push({x,y,map[x][y],map[x][y],0}); 22 while(!q.empty()) 23 { 24 now=q.front(); 25 q.pop();//出隊 26 x=now.x,y=now.y; 27 int c=now.c,p=now.p,s=now.s; 28 if(x==m && y==m) v[++tot]=p;//找到一個解就存入v陣列 29 for(int i=0;i<4;i++) 30 { 31 int tx=x+dx[i],ty=y+dy[i]; 32 if(tx>=1 && tx<=m && ty>=1 && ty<=m) 33 {//判斷是否越界 34 if(map[x][y]!=0 && map[x][y]==map[tx][ty] && mark[tx][ty]>p) 35 { 36 mark[tx][ty]=p;//如果可以得到更小代價就更新,下同 37 q.push({tx,ty,map[tx][ty],map[tx][ty],p});//入隊 38 } 39 //第一種情況 40 else if(map[x][y]!=0 && map[tx][ty]!=0 && map[x][y]!=map[tx][ty] && mark[tx][ty]>p+1) 41 { 42 mark[tx][ty]=p+1; 43 q.push({tx,ty,map[tx][ty],map[tx][ty],p+1}); 44 } 45 //第二種情況 46 else if(map[x][y]!=0 && map[tx][ty]==0 && mark[tx][ty]>p+2) 47 { 48 mark[tx][ty]=p+2; 49 q.push({tx,ty,map[tx][ty],map[x][y],p+2}); 50 //注意,這種情況需要使用一次魔法,此時s記錄為未移動前的格子的顏色 51 } 52 //第三種情況 53 else if(map[x][y]==0 && map[tx][ty]==s && mark[tx][ty]>p) 54 { 55 mark[tx][ty]=p; 56 q.push({tx,ty,mark[tx][ty],mark[tx][ty],p}); 57 } 58 //第四種情況(1) 59 else if(map[x][y]==0 && map[tx][ty] && map[tx][ty]!=s && mark[tx][ty]>p+1) 60 { 61 mark[tx][ty]=p+1; 62 q.push({tx,ty,mark[tx][ty],mark[tx][ty],p+1}); 63 } 64 //第四種情況(2) 65 //如果這幾種情況都不符合,只剩下從無色格子到無色格子一種可能,不需考慮 66 } 67 } 68 } 69 return; 70 } 71 int main() 72 { 73 scanf("%d %d",&m,&n); 74 for(int i=1;i<=m;i++) 75 for(int j=1;j<=m;j++) mark[i][j]=0x7fffffff; 76 //將到達各個點的最小代價初始化為無窮大(0x7fffffff) 77 for(int i=1;i<=n;i++) 78 { 79 int a,b,cl; 80 scanf("%d %d %d",&a,&b,&cl); 81 map[a][b]=cl+1;//將紅色記為1,黃色記為2,無色則為0 82 } 83 bfs(1,1); 84 if(!tot) { printf("-1"); return 0; }//如果沒有解,輸出-1 85 ans=v[1]; 86 for(int i=2;i<=tot;i++) ans=min(ans,v[i]); 87 //比較並求出最小代價 88 printf("%d",ans); 89 return 0; 90 }