Codeforces Round #742 (Div. 2) F. One-Four Overload

6 篇文章 0 订阅

F. One-Four Overload

题意:
给你 n ∗ m n*m nm的网格, n < = 500 , m < = 500 n<=500,m<=500 n<=500,m<=500,有些格子是‘X’,有些格子是‘.’,题目保证网格的最外围一圈都是‘.’,现在我们需要在‘.’的格子中填1或者0,‘X’上的值,是它上下左右,四个方向的‘.’的格子的权值和。
问有没有一种填色方案,使得所有‘X’格子上的值都是5的倍数。有的话,输出构造方案。

思路:
比较显然的思路,我们按照‘X’格子四个方向上‘.’格子的数量分类。
如果为0,那么直接给’X’格子赋值 0 ;
如果为1或3,不管怎么填数也不可能实现是5的倍数。
如果是2,那么那两个格子只能一个填1,一个填4;
如果是4,那么只能填两个1,两个4;

考虑4的情况怎么填比较好,如果上面的颜色和左边的颜色一样,那么对于左上角的一个’X’,如果他周围只有两个,那么就不满足了。处于这样的考虑,我们把一个数量为4的格子,上面和下面填一种数字,左面和右面填一种数字,这样就保证了左上、左下,右上、右下四个方面的和为5的倍数。

现在这样看来,还是不太能处理。但是我们可以发现,我们对于一个格子,只能填1或4,而且一旦填了一个数字,那么相应的,下面的数字就唯一确定了。很想图的染色,用黑白两种颜色去染图,相邻的点染不同的颜色,可以比较简单的想到,不管第一个填什么数字,都是不影响的。那么我们就建一张图去染色即可。
(这样处理起来很方便,我的第一个想法就是把一个 ‘X’ ,一个 ‘.’ 这样的连通图扣出来,再去染色,换成黑白染色的写法就很好写,也很好懂了);

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long

int n,m;
vector<int>v[300050];
int d[][2]={1,0,0,1,-1,0,0,-1};
char mp[505][505];
int ans[300050]={0};
int id(int x,int y){
	return (x-1)*m+y;
}
int cnt(int x,int y){
	int res=0;
	for(int i=0;i<4;i++){
		int nx=x+d[i][0];
		int ny=y+d[i][1];
		if(mp[nx][ny]=='.')res++;
	}
	return res;
}
int f=1;
void dfs(int x,int w){
	ans[x]=w;
	for(int i=0;i<v[x].size();i++){
		if(!ans[v[x][i]])
			dfs(v[x][i],5-w);
		if(ans[v[x][i]]==ans[x])f=0;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>mp[i]+1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(mp[i][j]=='X'){
				int res=cnt(i,j);
				if(res==2){
					ans[id(i,j)]=5;
					int now=-1;
					for(int k=0;k<4;k++){
						int nx=i+d[k][0];
						int ny=j+d[k][1];
						if(mp[nx][ny]=='.'){
							if(now==-1)now=id(nx,ny);
							else {
								v[now].push_back(id(nx,ny));
								v[id(nx,ny)].push_back(now);
							}
						}
					}
				}else if(res==4){
					ans[id(i,j)]=10;
					v[id(i-1,j)].push_back(id(i,j-1));
					v[id(i-1,j)].push_back(id(i,j+1));
					v[id(i+1,j)].push_back(id(i,j+1));
					v[id(i+1,j)].push_back(id(i,j-1));

					v[id(i,j-1)].push_back(id(i-1,j));
					v[id(i,j+1)].push_back(id(i-1,j));
					v[id(i,j+1)].push_back(id(i+1,j));
					v[id(i,j-1)].push_back(id(i+1,j));
					
				}else if(res==0){
					ans[id(i,j)]=0;
				}else if(res%2==1){
					printf("NO\n");
					return 0;
				}
			}
		}
	}
	f=1;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(mp[i][j]=='.'&&!ans[id(i,j)])
				dfs(id(i,j),4);
		}
	}
	if(!f)cout<<"NO"<<endl;
	else{
		cout<<"YES\n"<<endl;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++)cout<<ans[id(i,j)]<<" ";
			cout<<endl;
		}
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值