洛谷 P3956 棋盘(BFS)
https://www.cnblogs.com/violet-acmer/p/9827010.html
题解:
BFS
相关变量解释:
color[maxn][maxn];...................................color[ i ][ j ] : ( i , j )点的颜色,-1代表无色
dp[maxn][maxn];.......................................dp[ i ][ j ] : 从( 1 , 1 )点到( i , j )点需要花费的最少金币
magic[maxn][maxn];..................................magic[ i ][ j ] : 判断 ( i , j )点是否使用魔法
in[maxn][maxn];.........................................in[ i ][ j ] : 判断( i , j )点是否在队列中
具体步骤:
初始,将(1,1)点加入队列中;
(1):保存队头元素并弹出
(2):每次,判断当前点的上下左右点是否可以通过当前点使dp[ ][ ]变小,如果可以,更新dp[ ][ ],如果被更新的点不在队列中,加入队列
(3):重复(2)过程,直到队列为空
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 using namespace std; 6 #define INF 0x3f3f3f3f 7 #define P pair<int ,int > 8 #define mem(a,b) memset(a,b,sizeof a) 9 const int maxn=100+50; 10 11 int m,n; 12 int color[maxn][maxn]; 13 int dp[maxn][maxn]; 14 bool marge[maxn][maxn]; 15 bool in[maxn][maxn]; 16 queue<P >myqueue; 17 int dx[4]={-1,1,0,0}; 18 int dy[4]={0,0,-1,1}; 19 bool isSat(int val) 20 { 21 return val >= 1 &&val <= m; 22 } 23 void Solve() 24 { 25 dp[1][1]=0;//初始化dp[1][1],并将其加入到队列中 26 myqueue.push(P(1,1)); 27 while(!myqueue.empty())//步骤(3) 28 { 29 P p=myqueue.front();//步骤(1) 30 myqueue.pop(); 31 in[p.first][p.second]=false; 32 for(int i=0;i < 4;++i)//步骤(2) 33 { 34 int x=p.first+dx[i]; 35 int y=p.second+dy[i]; 36 if(isSat(x) && isSat(y)) 37 { 38 if(!marge[p.first][p.second])//如果当前点的未使用过魔法的,也就意味这当前点的颜色是本身就有的 39 { 40 if(!marge[x][y])//如果与当前点相邻的点(x,y)也为曾使用过魔法 41 { 42 if(color[x][y] != -1)//如果点(x,y)有色,但并不是因为魔法而产生的 43 { 44 //如果当前点可以放缩dp[x][y] 45 if(dp[x][y] > dp[p.first][p.second]+(color[x][y] != color[p.first][p.second])) 46 { 47 dp[x][y]=dp[p.first][p.second]+(color[x][y] != color[p.first][p.second]); 48 if(!in[x][y])//被放缩的点(x,y)如果不在队列中,加入队列 49 in[x][y]=true,myqueue.push(P(x,y)); 50 } 51 } 52 else//如果无色,通过使用魔法将其变为与当前点颜色相同的点,并被放缩了dp[][] 53 { 54 marge[x][y]=true; 55 color[x][y]=color[p.first][p.second]; 56 dp[x][y]=dp[p.first][p.second]+2; 57 if(!in[x][y]) 58 in[x][y]=true,myqueue.push(P(x,y)); 59 } 60 } 61 else if(dp[x][y] > dp[p.first][p.second]+2)//如果点(x,y)在之前使用过魔法,就需要判断,当前为使用过魔法的点是否可以放缩dp[x][y] 62 { 63 dp[x][y]=dp[p.first][p.second]+2; 64 color[x][y]=color[p.first][p.second]; 65 if(!in[x][y]) 66 in[x][y]=true,myqueue.push(P(x,y)); 67 } 68 } 69 else if(!marge[x][y] && color[x][y] != -1)//如果当前点的使用过魔法的,也就意味这当前点的颜色是通过魔法变来的,那么,只有当其临近点(x,y)含有的颜色是其本身就有的才有资格判断是否可以被放缩 70 { 71 if(dp[x][y] > dp[p.first][p.second]+(color[x][y] != color[p.first][p.second])) 72 { 73 dp[x][y]=dp[p.first][p.second]+(color[x][y] != color[p.first][p.second]); 74 if(!in[x][y]) 75 myqueue.push(P(x,y)),in[x][y]=true; 76 } 77 } 78 } 79 } 80 } 81 printf("%d\n",dp[m][m] == INF ? -1:dp[m][m]); 82 } 83 void Init() 84 { 85 mem(dp,INF); 86 mem(in,false); 87 mem(marge,false); 88 mem(color,-1); 89 } 90 int main() 91 { 92 scanf("%d%d",&m,&n); 93 Init(); 94 for(int i=1;i <= n;++i) 95 { 96 int x,y; 97 scanf("%d%d",&x,&y); 98 scanf("%d",&color[x][y]); 99 } 100 Solve(); 101 }