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