1. 程式人生 > 實用技巧 >棋盤(BFS)

棋盤(BFS)

原題:

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 }