1. 程式人生 > >CodeForces - 616C詳解(很有意思的bfs)

CodeForces - 616C詳解(很有意思的bfs)

 思路:

很有意思的一道題,首先如果暴力遍歷"*",bfs(),執行到第11個測試點就超時了,因為點比較多,是1000*1000的格子,每次bfs一個點要進行4*10^6次運算(4是因為要上下左右找一次)。也就是最大運算是10^6*4*10^6等於4*10^12,遠超出計算機一次能運算的10^8.

正確思路:

遍歷" . ",把"."的連通區域bfs出來,給它賦上標記,表明這些點是一個連通區域,然後記錄這個連通區域的點個數,將標記與點個數作為一對存入map中,

這樣我們可以得到所有的連通區域(兩個連通區域不相接),對於每一個" * "來說,把它周圍四個方位的點所在的連通區域的點的個數加起來再加1就是總的連通點。但此時可能會有重複的點被算進來了,因為" * "的周圍的點可能就是同一個連通區域,所以,

我們採用set集合,把"*"周圍的點的標記放進去去重。

#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
int n,m;
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
int vis[1050][1050];
char mk[1050][1050];
struct node{
	int x,y;
}nextt;
map<int,int> mm;
queue<node> q;
set<int> ss;
set<int>::iterator it;
bool check(int x,int y){
	if(x<0||x>=n||y<0||y>=m) return 0;
	if(vis[x][y]) return 0;
	if(mk[x][y]!='.') return 0;
	return 1;
}
void bfs(int sx,int sy,int ans){
	int cnt=0;
	node p;
	p.x=sx,p.y=sy;
	q.push(p);
	vis[sx][sy]=ans;
	while(!q.empty()){
		node temp=q.front();
		q.pop();
		cnt++;
		for(int i=0;i<4;i++){
			nextt.x=temp.x+dx[i];
			nextt.y=temp.y+dy[i];
			if(check(nextt.x,nextt.y)){
				vis[nextt.x][nextt.y]=ans;
				q.push(nextt);
			}
		}
	}
	mm[ans]=cnt;//把同屬於一個連通域的'.'個數放入map中 
}
int main(){
	int sum=0;
		scanf("%d %d",&n,&m); 
		memset(vis,0,sizeof(vis));
		int cc=1;
		for(int i=0;i<n;i++) scanf("%s",mk[i]);
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++){ 
				if(mk[i][j]=='.'&&!vis[i][j]) bfs(i,j,cc++); 
			}
		}
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++){
				if(mk[i][j]=='*'){
					sum=1;
					ss.clear();
					if(i>0)	ss.insert(vis[i-1][j]);
					if(i<n-1) ss.insert(vis[i+1][j]);
					if(j>0) ss.insert(vis[i][j-1]);
					if(j<m-1) ss.insert(vis[i][j+1]);
					for(it=ss.begin();it!=ss.end();it++){
						sum+=mm[*it];
					}
					sum%=10;
					mk[i][j]=sum+'0';
				}
			}
		}
		for(int i=0;i<n;i++){
			printf("%s\n",mk[i]);
		}
	return 0;
}