【洛谷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;
}
待到再迷茫时回头望,所有脚印会发出光芒