題目大意:給你一個n*m的棋盤,有一些壞點不能走,你有很多軍隊,每支軍隊可以像象棋裏的馬一樣移動,不過馬是1*2移動的,而軍隊是r*c移動的,軍隊只能從上往下移動,如果一個點已經被一直軍隊經過,那麼其他軍隊不能再經過這個點,求覆蓋所有非壞點的最少軍隊數
對在某個點的軍隊可能跳的位置建邊。把圖摳出來,因爲軍隊只能從上往下移動,所以他移動的方式就是是一條鏈,所以答案就是把這個圖分成若干條鏈,求鏈的數量的最小值
這不就是二分圖匹配麼
對於一個鏈,除了端點,每個點都需要一個出邊一個入邊,跑匈牙利就行了
注意,每個點跑匈牙利之前都要清一次vis數組!才能正確表示某個點在這次匹配中是否已經被使用
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 60
#define mod 20100403
#define p(i,j) ((i-1)*m+j)
#define dd double
using namespace std;
char str[N][N];
int n,m,r,c,cte;
int mp[N][N],head[N*N],vis[N*N],mch[N*N];
int xx[4],yy[4];
struct Edge{int to,nxt;}edge[N*N*10];
void ae(int u,int v){
++cte,edge[cte].to=v;
edge[cte].nxt=head[u];
head[u]=cte;
}
int check(int x,int y){
if(x<1||y<1||x>n||y>m||mp[x][y]) return 0;
return 1;
}
void build_edge()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(mp[i][j]) continue;
for(int k=0;k<4;k++)
if(check(i+xx[k],j+yy[k]))
ae(p(i,j),p(i+xx[k],j+yy[k]));
}
}
int Hungary(int x)
{
for(int j=head[x];j!=-1;j=edge[j].nxt){
int v=edge[j].to;
if(!vis[v])
{
vis[v]=1;
if(!mch[v]||Hungary(mch[v])){
mch[v]=x;
return 1;
}
}
}return 0;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&r,&c);
for(int i=1;i<=n;i++){
scanf("%s",str[i]+1);
for(int j=1;j<=m;j++)
if(str[i][j]=='.') mp[i][j]=0;
else mp[i][j]=1;
}
xx[0]=r,xx[1]=r,xx[2]=c,xx[3]=c;
yy[0]=c,yy[1]=-c,yy[2]=r,yy[3]=-r;
int ans=0;
memset(head,-1,sizeof(head));
build_edge();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(mp[i][j]) continue;
memset(vis,0,sizeof(vis));
if(!Hungary(p(i,j))) ans++;}
printf("%d\n",ans);
return 0;
}