题目链接:BZOJ 2150
这道题应该是裸的最小路径覆盖。我们知道:最小路径覆盖=总结点数-最大匹配数。对于二分图的最大匹配数,可以用最大流求,也可以用匈牙利算法求。但是我现在还不会匈牙利算法(请神犇自动无视),所以我用dinic写的。对于这道题,最后的答案就是: ‘.’的个数-最大流。
建图:把每个节点拆成两个点,u(流入),v(流出),加入源点S,和汇点T。将S与v连边,u与T连边,u与马步所到的点的流出的点连边,这些边的容量都为1。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
#define inf (1e9)
int M,N,R,C,S,T,tot=0;
int map[100][100],head[5010],dep[5010];
struct node{
int u,v,c,next;
}e[200000];
int k=0;
void adde(int u,int v,int c){
e[k].u=u; e[k].v=v; e[k].c=c; e[k].next=head[u]; head[u]=k++;
e[k].u=v; e[k].v=u; e[k].c=0; e[k].next=head[v]; head[v]=k++;
}
bool bfs(){
queue<int>q;
memset(dep,-1,sizeof(dep));
dep[S]=0; q.push(S);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].v;
if(dep[v]==-1&&e[i].c){
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]!=-1;
}
int dfs(int a,int b){
if(a==T)return b;
int ret=0;
for(int i=head[a];i!=-1;i=e[i].next){
int v=e[i].v;
if(dep[v]==dep[a]+1&&e[i].c>0){
int x=dfs(v,min(b-ret,e[i].c));
e[i].c-=x;
e[i^1].c+=x;
ret+=x;
if(ret==b)return ret;
}
}
if(ret==0)dep[a]=-1;
return ret;
}
void dinic(){
int ret=0;
while(bfs()){
ret+=dfs(S,inf);
}
printf("%d",tot-ret);
}
void input(){
scanf("%d%d%d%d",&M,&N,&R,&C);
for(int i=1;i<=M;i++){
scanf("\n"); char x;
for(int j=1;j<=N;j++){
scanf("%c",&x);
if(x=='.')map[i][j]=1,tot++; else map[i][j]=0;
}
}
}
void solve(){
memset(head,-1,sizeof(head));
S=0; T=2*N*M+1;
for(int i=1;i<=M;i++)
for(int j=1;j<=N;j++){
if(map[i][j]==0)continue;
adde(S,(i-1)*N+j,1);
adde((i-1)*N+j+N*M,T,1);
if(R==C){
if(map[i+R][j+R])adde((i-1)*N+j,(i+R-1)*N+j+R+N*M,1);
if(j>R&&map[i+R][j-R])adde((i-1)*N+j,(i+R-1)*N+j-R+N*M,1);
}
else{
if(map[i+R][j+C])adde((i-1)*N+j,(i+R-1)*N+j+C+N*M,1);
if(map[i+C][j+R])adde((i-1)*N+j,(i+C-1)*N+j+R+N*M,1);
if(j>C&&map[i+R][j-C])adde((i-1)*N+j,(i+R-1)*N+j-C+N*M,1);
if(j>R&&map[i+C][j-R])adde((i-1)*N+j,(i+C-1)*N+j-R+N*M,1);
}
}
dinic();
}
int main(){
input();
solve();
return 0;
}