把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷2172】[国家集训队] 部落战争(最小路径覆盖)

点此看题面

  • 给定一张\(n\times m\)的图,有些格子上有障碍。
  • 规定一支军队可以从任意格子出发,只能向下走\(R\times C\)\(C\times R\)的路线,且不能经过其他军队走过的格子或有障碍的格子。
  • 求最少需要多少军队才能走过所有没障碍的格子。
  • \(n,m\le50,R,C\le10\)

最小路径覆盖

一看就是一道最小路径覆盖裸题啊!

考虑我们把一个点拆成入点和出点两个,如果从\(x\)能走到\(y\),就从\(x\)的出点向\(y\)的入点连一条边。

然后我们发现,我们选择了一条边,就相当于合并了两条路径(从某个点到\(x\)的路径和从\(y\)到某个点的路径),能使答案减少\(1\),而显然每个出点和入点都只能被选择一次。

所以答案就应该是总点数-二分图最大匹配,直接匈牙利算法即可。

代码:\(O(n^2m^2)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50
#define P(i,j) (((i)-1)*m+(j))
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,R,C;char a[N+5][N+5];
int ee,lnk[N*N+5];struct edge {int to,nxt;}e[4*N*N+5];
int s[N*N+5],vis[N*N+5];I bool H(CI x,CI ti)//匈牙利算法
{
	for(RI i=lnk[x];i;i=e[i].nxt)
	{
		if(vis[e[i].to]==ti) continue;vis[e[i].to]=ti;
		if(!s[e[i].to]||H(s[e[i].to],ti)) return s[e[i].to]=x,true;
	}return false;
}
int main()
{
	RI i,j;for(scanf("%d%d%d%d",&n,&m,&R,&C),i=1;i<=n;++i) scanf("%s",a[i]+1);
	#define Ex(x,y) ((x)<=n&&1<=(y)&&(y)<=m&&a[x][y]=='.')//判断一个点是否存在
	for(i=1;i<=n;++i) for(j=1;j<=m;++j) a[i][j]=='.'&&//建图
	(
		Ex(i+R,j+C)&&(add(P(i,j),P(i+R,j+C))),Ex(i+C,j+R)&&(add(P(i,j),P(i+C,j+R))),
		Ex(i+R,j-C)&&(add(P(i,j),P(i+R,j-C))),Ex(i+C,j-R)&&(add(P(i,j),P(i+C,j-R)))
	);
	RI t=0;for(i=1;i<=n;++i) for(j=1;j<=m;++j) a[i][j]=='.'&&(t+=!H(P(i,j),P(i,j)));//总点数-二分图最大匹配
	return printf("%d\n",t),0;
}
posted @ 2021-03-25 14:09  TheLostWeak  阅读(39)  评论(0编辑  收藏  举报