--- title: Codeforce 1105 D. Kilani and the Game 解析(裸BFS、實作) description: "Codeforce 1105 D. Kilani and the Game 解析(裸BFS、實作)" disqus: hackmd --- <meta name="robots" content="Codeforce 1105 D. Kilani and the Game 解析(裸BFS、實作)"> <!-- toc --> # Codeforce 1105 D. Kilani and the Game 解析(裸BFS、實作) 今天我們來看看CF1105D [題目連結](https://codeforces.com/problemset/problem/1105/D) > **題目** 給一個$n\times m$的地圖,地圖上有幾種格子:空地、路障、某個玩家的某些城堡。(可能有$1\le p\le9$個玩家) 給定一開始每個玩家至少有一個城堡,玩家照順序移動,每個玩家有自己的移動步數,求最後每個玩家能有幾個城堡。 ### 前言 寫這題一直TLE,搞了好幾個小時才發現如果每次BFS都宣告一個新的queue,那麼可能會因為allocation的cost太大而TLE,並且還有一些其他的奇怪問題(此處不提)。總之,我們應該極力避免頻繁declare queue。 ### 想法 顯然就是BFS,但是實作上要注意:queue中存的是$\{vertex,剩下的步數\}$,由於我們每回合開始的時候,一定會有一些點在queue裡(上回合遺留下來的,實作上我們每回合結束時把沒有移動步數的格子放入全域變數等下回合push進queue裡),我們要同時防止其他玩家走到這一點,又要讓擁有這個格子的玩家在自己的回合時不會判斷這個格子不能走,我們有兩種實作方式: 1. 在push入新格子進queue之前先把格子標示為$visited$,BFS從新格子開始時就不要檢查是否走過了(也就是只在往外走時檢查是否走過)。 這種方法可以某種程度上縮減queue中多餘的元素數量,是比較好的方法。 2. push入queue時還不會標為$visited$。每次從新的格子開始BFS時,除非這個格子的移動數量$>0$,否則不要標記為$visited$(但是地圖上已經寫上去了)。 這種方法比較糟糕,沒什麼結構。 ### 程式碼(法1): ```cpp= const int _n=1010; struct ele{int x,y,move;}; int t,n,m,s[11],p,mp[_n][_n],ans[11]; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; queue<ele> st[11],q; bool vis[_n][_n]; char c,a[_n][_n]; main(void) { scanf("%d%d%d",&n,&m,&p); rep(i,1,p+1)scanf("%d",&s[i]); rep(i,1,n+1)scanf("%s",a[i]+1); rep(i,1,n+1)rep(j,1,m+1){ c=a[i][j];int res=c;if(c=='.')res='0'+0;if(c=='#')res='0'+10; mp[i][j]=res-'0'; if(mp[i][j]!=0 and mp[i][j]!=10)st[mp[i][j]].push({i,j,s[mp[i][j]]}),vis[i][j]=1; if(mp[i][j]==10)vis[i][j]=1; } while(1){ bool OUT=1;rep(i,1,p+1){if(!st[i].empty())OUT=0;} if(OUT)break; rep(id,1,p+1){ while(!st[id].empty())q.push(st[id].front()),st[id].pop(); ele now;int nx,ny; while(!q.empty()){ now=q.front(); q.pop(); int x=now.x,y=now.y,mv=now.move; mp[x][y]=id; if(mv==0)st[id].push({x,y,s[id]}); else rep(j,0,4){ nx=x+dx[j],ny=y+dy[j]; if(nx<1||nx>n||ny<1||ny>m||vis[nx][ny]||mp[nx][ny]||mv<=0)continue; vis[nx][ny]=1; q.push({nx,ny,mv-1}); } } } } rep(i,1,n+1)rep(j,1,m+1)ans[mp[i][j]]++; rep(i,1,p+1)printf("%d ",ans[i]); return 0; } ``` 標頭、模板請點Submission看 [Submission](https://codeforces.com/contest/1105/submission/90444225) ### 程式碼(法2): ```cpp= const int _n=1010; struct ele{int x,y,move;}; int t,n,m,s[20],p,mp[_n][_n],ans[20]; queue<ele> st[20],q; bool vis[_n][_n]; char c; main(void) {cin.tie(0);ios_base::sync_with_stdio(0); cin>>n>>m>>p;rep(i,1,p+1)cin>>s[i]; cin.get();rep(i,1,n+1){rep(j,1,m+1){ c=cin.get();int res=c;if(c=='.')res='0'+0;if(c=='#')res='0'+10; mp[i][j]=res-'0'; assert(mp[i][j]<11); if(mp[i][j]!=0 and mp[i][j]!=10)st[mp[i][j]].push({i,j,s[mp[i][j]]}); if(mp[i][j]==10)vis[i][j]=1; }cin.get();} rep(i,0,n+1)mp[i][0]=10; rep(i,0,m+1)mp[0][i]=10; rep(i,0,n+1)mp[i][m+1]=10; rep(i,0,m+1)mp[n+1][i]=10; while(1){ bool OUT=1;rep(i,1,p+1){assert(i<11);if(!st[i].empty())OUT=0;} if(OUT)break; rep(id,1,p+1){ assert(id<11); while(!st[id].empty())q.push(st[id].front()),st[id].pop(); while(!q.empty()){ ele now=q.front(); q.pop(); if(vis[now.x][now.y])continue; int x=now.x,y=now.y,mv=now.move; mp[x][y]=id; if(mv>0)vis[x][y]=1; if(mv==0)st[id].push({x,y,s[id]}); if(mv>0 and !mp[x-1][y])q.push({x-1,y,mv-1}); if(mv>0 and !mp[x+1][y])q.push({x+1,y,mv-1}); if(mv>0 and !mp[x][y-1])q.push({x,y-1,mv-1}); if(mv>0 and !mp[x][y+1])q.push({x,y+1,mv-1}); } } } rep(i,1,n+1)rep(j,1,m+1)ans[mp[i][j]]++; rep(i,1,p+1)cout<<ans[i]<<' '; cout<<'\n'; return 0; } ``` 標頭、模板請點Submission看 [Submission](https://codeforces.com/contest/1105/submission/90474066)