【图论-二分图】中山纪念中学暑期游Day15——【NOIP2013模拟联考3】沙耶的玩偶(doll)

前言

Oh,昨天刚讲二分图,今天就考,醉了

题目

Time Limits: 1000 ms  Memory Limits: 131072 KB

在美鱼和理树后援团拯救世界的同时,外表柔弱的理树也开始坚强起来,思考着离开这个世界的办法。误打误撞地,她遇上了正在教室破坏课桌打开迷宫入口的沙耶。沙耶告诉理树,这个世界的出口就是这个迷宫的出口。于是理树毫不犹豫地跟沙耶一起跳进了迷宫。在迷宫里,两个女孩子互帮互助,一会儿割绳子,一会儿泡温泉,一会儿雕冰块,跌跌撞撞地走到了终点。不出所料,终点也有一个机关在等着她们。

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

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

Input

第一行包含4 个整数M、N、R、C,意义见问题描述。接下来M 行每行一个长度为N 的

字符串。如果某个字符是'.',表示这个地方有一个玩偶;如果这个字符是'x',表示这个地

方是坑。

Output

输出一个整数,表示最短时间。

Sample Input

3 3 1 2

...

.x.

...

Sample Output

4

【数据范围】

30%的数据中,1<=M,N<=4,1<=R,C<=3。

70%的数据中,1<=M<=20,1<=N<=4,1<=R,C<=3。

100%的数据中,1<=M,N<=50,1<=R,C<=10。

【图例】

分析

参考来源:https://blog.csdn.net/SSLGZ_yyc/article/details/99641246

感觉已经讲的很清楚了(反正我这个蒟蒻看懂了

当前机器人在格子(X,Y),即第 X 行第 Y 列,那么它能到达的地方最多四个:(X+R,Y+C),(X+R,Y-C),(X+C,Y+R),(X+C,Y-R)。
那么把问题求机器人路径覆盖整个盘子想象成求整个图的最小路径覆盖。而且观察
此题是有向无环图,所以可以使用二分图最大匹配的方法。
从某个点去更新下一个点,当某个点被匹配了之后,考虑最大图匹配中的向后跳,也就是把之前更新它的点的向其他地方跳。
左边建立 MN个点,右边建立 MN 个点。如果(X,Y)可以跳向(X’,Y’),那么就
让左边的(X,Y)向右边的(X’,Y’)连一条边。
建好图后,做一次最大匹配。那么最终答案就是空格子个数-最大匹配数。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=50,N=2501;
char ch[N+5][N+5];
int a[N+5][N+5],head[N+5],b[N*2+5];
bool vis[N*2+5];
int n,m,r,c,tot,cnt,ans;
struct edge
{
	int to,next;
}e[6250000+5];
void add(int u,int v)
{
	e[++cnt].next=head[u];
	e[cnt].to=v;
	head[u]=cnt;
}
bool check(int u)
{
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to;
		if(vis[v])
		{
			vis[v]=0;
			if(b[v]==0||check(b[v]))
			{
				b[v]=u;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&r,&c);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch[i]+1);
		for(int j=1;j<=m;j++)
			if(ch[i][j]=='.')
			{
				a[i][j]=++tot;
				if(i>r&&j>c&&a[i-r][j-c]>=1)
					add(tot,a[i-r][j-c]+N);
				if(i>r&&j+c<=m&&a[i-r][j+c]>=1)
					add(tot,a[i-r][j+c]+N);
				if(i>c&&j>r&&a[i-c][j-r]>=1)
					add(tot,a[i-c][j-r]+N);
				if(i>c&&j+r<=m&&a[i-c][j+r]>=1)
					add(tot,a[i-c][j+r]+N);
			}
	}
	for(int i=1;i<=tot;i++)
	{
		for(int j=1;j<=tot;j++)
			vis[N+j]=true;
		if(check(i)==false)
			ans++;
	}
	printf("%d",ans);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值