codeforces1345DMonopole Magnets题解

1 篇文章 0 订阅

codeforces1345DMonopole Magnets

题目镜像

https://codeforces.ml/contest/1345/problem/D

题意

在一个格子图上,有黑白两种格子。初始时某些格子上有S棋子或N棋子,或两个都有一些。每次N棋子都会向某个与其同行同列的S棋子动一格。S棋子不可能动。要求构造一格初始的NS棋子的填法,满足下面条件:
1.每行每列都至少一个S棋子。
2.无论N棋子怎么走都不会走到白格子上去。
3.对每个黑格子,存在某一种N棋子走动的方案使得某个N棋子能走到这个黑格子上。
给定黑白地图,输出最少的N棋子数(S棋子不限)

思路

可以发现每种合法方案一定满足一些性质:
1.每个黑色的连通块至少一个N。
2.每个黑连通块的N必须且只能在本连通块内走到所有格子。(黑连通块全放上S就能保证走遍所有本连通块格子)。
3.为满足2,任意行列都只能有一段连续的黑格,不能有黑格夹住白格子的情况。
4.如果要在白格子填S的话,这个白格子所在行列都要纯白。
这样如果存在答案,就是连通块的数目
妈的我4情况没考虑清楚,以为白格子不能填S,WA17到怀疑人生。

代码

#include<cstdio>
#include<cstring>
using namespace std;
const int NN=1010;
int dei[4]={1,-1,0,0};
int dej[4]={0,0,1,-1};
int vis[NN][NN];
char a[NN][NN];
int n,m;
int pd(int x,int y){
	if((x<1)||(x>n)||(y<1)||(y>m))return 0;
	if(vis[x][y])return 0;
	if(a[x][y]=='.')return 0;
	return 1;
}
struct Node{
	int i,j;
}q[NN*NN];
int ans=0;
int fi[NN],fj[NN];
int markk(int x,int y){
	if(fi[x]!=0&&fi[x]!=ans){
		return 0;
	}
	else fi[x]=ans;
	if(fj[y]!=0&&fj[y]!=ans){
		return 0;
	}
	else fj[y]=ans;
	return 1;
}
int bfs(int sti,int stj){
	int head=1,tail=0;
	q[++tail].i=sti;
	q[tail].j=stj;
	vis[sti][stj]=1;
	int temp=markk(sti,stj);
	if(temp==0)return -1;
	while(head<=tail){
		Node cur=q[head];
		for(int i=0;i<4;i++){
			Node neww;neww.i=cur.i+dei[i];neww.j=cur.j+dej[i];
			if(pd(neww.i,neww.j)==1){
				q[++tail]=neww;
				vis[neww.i][neww.j]=1;
				temp=markk(neww.i,neww.j);
				if(temp==0)return -1;
			}
		}
		head++;
	}
	return 1;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",a[i]+1);
	}
	int can=1;
	int hav1=0;
	for(int i=1;i<=n;i++){
		int l=n+1,r=0;
		for(int j=1;j<=m;j++){
			if(i==1&&a[i][j]=='#')hav1=1;
			if(a[i][j]=='#'){l=j;break;}
		}
		for(int j=m;j>=1;j--){
			if(a[i][j]=='#'){r=j;break;}
		}
		if(l<r){
			for(int j=l+1;j<r;j++){
				if(a[i][j]=='.'){
					can=-1;
					break;
				}
			}
		}
		if(can==-1)break;
	}
	if(can==-1){
		//if(n==1000&&m==1000&&hav1==0)printf("1");
		printf("-1\n");
		return 0;
	}
	for(int j=1;j<=m;j++){
		int l=n+1,r=0;
		for(int i=1;i<=n;i++){
			if(a[i][j]=='#'){l=i;break;}
		}
		for(int i=n;i>=1;i--){
			if(a[i][j]=='#'){r=i;break;}
		}
		if(l<r){
			for(int i=l+1;i<r;i++){
				if(a[i][j]=='.'){
					can=-1;
					break;
				}
			}
		}
		if(can==-1)break;
	}
	if(can==-1){
		//if(n==1000&&m==1000&&hav1==0)printf("2");
		printf("-1\n");
		return 0;
	}
	ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(!vis[i][j]&&a[i][j]=='#'){
				ans++;
				int res=bfs(i,j);
				if(res==-1){
					ans=-1;
					break;
				}
			}
		}
	}
	if(ans==0){printf("0\n");return 0;}
	int cant1=0,cant2=0;
	for(int i=1;i<=n;i++)if(fi[i]==0){
		cant1=1;
		break;
	}
	if(ans>=0)for(int i=1;i<=m;i++)if(fj[i]==0){
		cant2=1;
		break;
	}
	if(cant1+cant2==1){
		ans=-1;
	}
	if(ans>=0){
		printf("%d\n",ans);
	}
	else{
		printf("-1\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值