BZOJ 4554 &&HEOI2016 游戏

/*

在2016年,佳缘姐姐喜欢上了一款游戏,叫做泡泡堂。简单的说,这个游戏就是在一张地图上放上若干个炸弹,看是否能炸到对手,或者躲开对手的炸弹。在玩游戏的过程中,小H想到了这样一个问题:当给定一张地图,在这张地图上最多能放上多少个炸弹能使得任意两个炸弹之间不会互相炸到。炸弹能炸到的范围是该炸弹所在的一行和一列,炸弹的威力可以穿透软石头,但是不能穿透硬石头。给定一张n*m的网格地图:其中*代表空地,炸弹的威力可以穿透,可以在空地上放置一枚炸弹。x代表软石头,炸弹的威力可以穿透,不能在此放置炸弹。#代表硬石头,炸弹的威力是不能穿透的,不能在此放置炸弹。例如:给出1*4的网格地图*xx*,这个地图上最多只能放置一个炸弹。给出另一个1*4的网格地图*x#*,这个地图最多能放置两个炸弹。现在小H任意给出一张n*m的网格地图,问你最多能放置多少炸弹。

*///题目背景

/*

第一行输入两个正整数n,m,n表示地图的行数,m表示地图的列数。1≤n,m≤50。接下来输入n行m列个字符,代表网格地图。*的个数不超过n*m个。

*///样例输入

类似于之前做过的一道二分图,读入map后,给图染色,横向不间断的***或x*x只要不出现#,就一直附成一个值,然后再纵向染色,同横向,然后就是建图了,首先,因为x也染了色,但是却不能放炸弹,所以需要判断是不是*,是的话,就把该点的横向颜色与纵向颜色连一条边,然后跑最大匹配。

代码

//By AcerMo
#include<cmath>
#include<cstdio>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std; 
const int M=1050;
int n,m;
int t=0,ti=0;
char mmp[150][150];
int mat[M*5],vis[M*5];
int hcol[M][M],zcol[M][M];
int to[M*20],nxt[M*20],head[M*20],cnt;
inline void add(int x,int y)
{
	to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;
	return ;
}
inline bool mid(int x)
{
	for (int i=head[x];i;i=nxt[i])
	{
		if (vis[to[i]]) continue;
		vis[to[i]]=1;
		if (!mat[to[i]]||mid(mat[to[i]])) 
		return mat[to[i]]=x,1;
	}
	return 0;
}
inline void getpath()
{
	for (int i=1;i<=n;i++)
	for (int k=1;k<=m;k++)
	if (mmp[i][k]=='*') add(hcol[i][k],zcol[i][k]);
	return ;
}
inline void built()
{
	for (int i=1;i<=m;i++) mmp[0][i]='#';
	for (int i=1;i<=n;i++) mmp[i][0]='#';
	for (int i=1;i<=n;i++)
	for (int k=1;k<=m;k++)
	{
		if ((mmp[i][k]=='*'||mmp[i][k]=='x')&&(mmp[i][k-1]=='*'||mmp[i][k-1]=='x')) hcol[i][k]=ti;
		else if ((mmp[i][k]=='*'||mmp[i][k]=='x')&&mmp[i][k-1]=='#')  hcol[i][k]=++ti;
	}
	t=ti;ti++;
	for (int i=1;i<=m;i++)
	for (int k=1;k<=n;k++)
	{
		if ((mmp[k][i]=='*'||mmp[k][i]=='x')&&(mmp[k-1][i]=='x'||mmp[k-1][i]=='*')) zcol[k][i]=ti;
		else if ((mmp[k][i]=='*'||mmp[k][i]=='x')&&mmp[k-1][i]=='#') zcol[k][i]=++ti;
	}
	getpath();
	return ;
}
inline void clean()
{
	memset(vis,0,sizeof(vis));
	return ;
}
signed main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) scanf("%s",mmp[i]+1);
	built();int ans=0;
	for (int i=1;i<=t;clean(),i++) 
	if (mid(i)) ans++;
	cout<<ans;
	return 0;
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值