【bzoj4808】马&&【bzoj3175】[Tjoi2013]攻击装置

Description

众所周知,马后炮是中国象棋中很厉害的一招必杀技。"马走日字"。本来,如果在要去的方向有别的棋子挡住(俗
称"蹩马腿"),则不允许走过去。为了简化问题,我们不考虑这一点。马跟马显然不能在一起打起来,于是rly在
一天再次借来了许多许多的马在棋盘上摆了起来……但这次,他实在没兴趣算方案数了,所以他只想知道在N×M的
矩形方格中摆马使其互不吃到的情况下的最多个数。但是,有一个很不幸的消息,rly由于玩得太Happy,质量本来
就不好的棋盘被rly弄坏了,不过幸好只是破了其中的一些格子(即不能再放子了),问题还是可以继续解决的。

Input

一行,两个正整数N和M。
接下来N行,每行M个数,要么为0,表示没坏,要么为1,表示坏了。
N<=200,M<=200

Output

一行,输出最多的个数。

Sample Input

2 3
0 1 0
0 1 0

Sample Output

2
【解析】
最大独立集
把能攻击到的地方连边
总点数-最大匹配数=最大独立集
但是!!二分匹配出来的最大匹配数要/2

因为x能攻击y,连一条边;y也能攻击x,也会连一条边

(额~bzoj3175和bzoj4808没什么区别,数据范围都一样,只是m*n的棋盘和n*n的棋盘而已)

【bzoj4080】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
	int x,y,next;
}a[410000];int len,last[41000];
void ins(int x,int y)
{
	len++;
	a[len].x=x;a[len].y=y;
	a[len].next=last[x];last[x]=len;
}
int match[41000];
bool chw[41000];
bool find_muniu(int x)
{
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(chw[y]==false)
		{
			chw[y]=true;
			if(match[y]==0 || find_muniu(match[y])==true)
			{
				match[y]=x;
				return true;
			}
		}
	}
	return false;
}
int d[210][210];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	len=0;memset(last,0,sizeof(last));
	memset(d,-1,sizeof(d));
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++) scanf("%d",&d[i][j]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(d[i][j]==0)
			{
				int now=(i-1)*m+j;
				if(j-1>=1 && i-2>=1 && d[i-2][j-1]==0) ins(now,now-m-m-1);
				if(j-2>=1 && i-1>=1 && d[i-1][j-2]==0) ins(now,now-m-2);
				if(j+1<=m && i-2>=1 && d[i-2][j+1]==0) ins(now,now-m-m+1);
				if(j+2<=m && i-1>=1 && d[i-1][j+2]==0) ins(now,now-m+2);
				if(j-1>=1 && i+2<=n && d[i+2][j-1]==0) ins(now,now+m+m-1);
				if(j-2>=1 && i+1<=n && d[i+1][j-2]==0) ins(now,now+m-2);
				if(j+1<=m && i+2<=n && d[i+2][j+1]==0) ins(now,now+m+m+1);
				if(j+2<=m && i+1<=n && d[i+1][j+2]==0) ins(now,now+m+2);
			}
		}
	}
	int ans=0,k=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(d[i][j]==0)
			{
				k++;
				memset(chw,false,sizeof(chw));
				int now=(i-1)*m+j;
				if(find_muniu(now)==true) ans++;
			}
		}
	}
	printf("%d\n",k-ans/2);
	return 0;
}

【bzoj3175】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
	int x,y,next;
}a[410000];int len,last[41000];
void ins(int x,int y)
{
	len++;
	a[len].x=x;a[len].y=y;
	a[len].next=last[x];last[x]=len;
}
int match[41000];
bool chw[41000];
bool find_muniu(int x)
{
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(chw[y]==false)
		{
			chw[y]=true;
			if(match[y]==0 || find_muniu(match[y])==true)
			{
				match[y]=x;
				return true;
			}
		}
	}
	return false;
}
char s[210];
int d[210][210];
int main()
{
	int n;
	scanf("%d",&n);
	len=0;memset(last,0,sizeof(last));
	memset(d,-1,sizeof(d));
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		for(int j=1;j<=n;j++)
		{
			if(s[j]=='0') d[i][j]=0;
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(d[i][j]==0)
			{
				int now=(i-1)*n+j;
				if(j-1>=1 && i-2>=1 && d[i-2][j-1]==0) ins(now,now-n-n-1);
				if(j-2>=1 && i-1>=1 && d[i-1][j-2]==0) ins(now,now-n-2);
				if(j+1<=n && i-2>=1 && d[i-2][j+1]==0) ins(now,now-n-n+1);
				if(j+2<=n && i-1>=1 && d[i-1][j+2]==0) ins(now,now-n+2);
				if(j-1>=1 && i+2<=n && d[i+2][j-1]==0) ins(now,now+n+n-1);
				if(j-2>=1 && i+1<=n && d[i+1][j-2]==0) ins(now,now+n-2);
				if(j+1<=n && i+2<=n && d[i+2][j+1]==0) ins(now,now+n+n+1);
				if(j+2<=n && i+1<=n && d[i+1][j+2]==0) ins(now,now+n+2);
			}
		}
	}
	int ans=0,k=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(d[i][j]==0)
			{
				k++;
				memset(chw,false,sizeof(chw));
				int now=(i-1)*n+j;
				if(find_muniu(now)==true) ans++;
			}
		}
	}
	printf("%d\n",k-ans/2);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值