[Jzoj]3457. 沙耶的玩偶

题目描述

一个 m × n m\times n m×n 的方格棋盘,在有些格子上放了一个玩偶,而有些地方直接挖了个大坑。只有取走所有玩偶才能打开出口。但是,由于奇怪的设定,理树和沙耶不能直接触碰玩偶,他们需要操纵机器人来收集它。机器人的走法很奇怪,和国际象棋的马有点像,只不过马可以走任意方向的 1 ∗ 2 1*2 12 路线,它们只会由上往下走 r ∗ c r*c rc(或 c ∗ r c*r cr)的路线,不能回头。而机器人一旦经过一个有玩偶的格子,那个格子上的玩偶将被回收,并且在机器人离开时,那个格子会变成一个坑。理树可以把机器人放在任何一个有玩偶的格子上作为起点,也可以在任何一个有玩偶的格子回收机器人。机器人行走可以视为瞬移,只不过每一次设置新起点都会消耗 1 1 1 时间。并且,有坑的格子不能落脚。

就在这个紧要关头,玩偶狂热爱好者的沙耶却流着口水智商归0。理树不得不转而求助你,帮忙计算出最少多少时间就能收集到所有玩偶。

题目解析

是个二分图,把每个空地格子拆为两个点。

枚举每个有玩偶的格子作为起点,尝试扩展到四个方向,连边。

然后求出最大匹配。

a n s = ans= ans=空地格子数 − - 最大匹配

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,r,c,ans,cnt;
int link[2505],num[55][55];
char a[55][55];
vector<int> b[2505];
bool flag[2505];
void build(int x,int y) {b[x].push_back(y);}
bool find(int x)
{
	for(int i=0;i<b[x].size();i++)
	 if(!flag[b[x][i]])
	 {
	   int j=b[x][i];
	   flag[j]=1;
	   int q=link[j];
	   link[j]=x;
	   if(q==0||find(q)) return true;
	   link[j]=q;
	 }
	return false;
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&r,&c);
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	 {
	   cin>>a[i][j];
	   if(a[i][j]=='.')
	    num[i][j]=++cnt; 
	 }
	int x,y;
	for(int i=1;i<=n;i++)
	 for(int j=1;j<=m;j++)
	  if(a[i][j]=='.')
	  {
	    x=i+r;y=j+c;
	    if(x>=1&&x<=n&&y>=1&&y<=m&&a[x][y]=='.') build(num[i][j],num[x][y]);
	    x=i+r;y=j-c;
	    if(x>=1&&x<=n&&y>=1&&y<=m&&a[x][y]=='.') build(num[i][j],num[x][y]);
	    x=i+c;y=j+r;
	    if(x>=1&&x<=n&&y>=1&&y<=m&&a[x][y]=='.') build(num[i][j],num[x][y]);
	    x=i+c;y=j-r;
	    if(x>=1&&x<=n&&y>=1&&y<=m&&a[x][y]=='.') build(num[i][j],num[x][y]);
	  }
	for(int i=1;i<=cnt;i++)
	{
	  memset(flag,0,sizeof(flag));
	  ans+=find(i);
	}
	cout<<cnt-ans;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值