bzoj 2150
然后考虑正解
我们发现,最坏情况就是每个点都派驻军队,所以答案至多是“.”的数目
而且,每个点都至多只有一个入度和一个出度,所以我们可以将每个点拆成两个点,一个作为入点,一个作为出点,然后所有图上能到达的点由出点向入点建图
这样整个图就形成了一个二分图
然后在整个图上跑二分图匹配即可
答案即为“.”点数-二分图最大匹配数
稍微证明一下:我们的目的是最大化有入边的点的数量,那我们仅需让入点能更多的被匹配上即可
那也就是二分图匹配喽
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; struct Edge { int next; int to; }edge[10005]; int head[2505]; int maps[55][55]; char s[55]; bool used[6005]; int f[6005]; int n,m,r,c; int cnt=1,cot; queue <int> Q; bool check(int x,int y) { if(x>0&&x<=n&&y>0&&y<=m&&maps[x][y]) { return 1; } return 0; } void init() { memset(head,-1,sizeof(head)); cnt=1; } void add(int l,int r) { edge[cnt].next=head[l]; edge[cnt].to=r; head[l]=cnt++; } int po(int x,int y) { return (x-1)*m+y; } int ppo(int x,int y) { return (x-1)*m+y+n*m; } bool dfs(int x) { for(int i=head[x];i!=-1;i=edge[i].next) { int to=edge[i].to; if(used[to]) { continue; } used[to]=1; if(!f[to]||dfs(f[to])) { f[to]=x; return 1; } } return 0; } int hungary() { int ret=0; while(!Q.empty()) { memset(used,0,sizeof(used)); int u=Q.front(); Q.pop(); if(dfs(u)) { ret++; } } return ret; } int main() { freopen("legion.in","r",stdin); freopen("legion.out","w",stdout); scanf("%d%d%d%d",&n,&m,&r,&c); init(); for(int i=1;i<=n;i++) { scanf("%s",s+1); for(int j=1;j<=m;j++) { if(s[j]=='.') { maps[i][j]=1; Q.push(po(i,j)); cot++; }else { maps[i][j]=0; } } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { int x0=i+r; int y0=j+c; if(check(x0,y0)) { add(po(i,j),ppo(x0,y0)); } x0=i+r; y0=j-c; if(check(x0,y0)) { add(po(i,j),ppo(x0,y0)); } x0=i+c; y0=j+r; if(check(x0,y0)) { add(po(i,j),ppo(x0,y0)); } x0=i+c; y0=j-r; if(check(x0,y0)) { add(po(i,j),ppo(x0,y0)); } } } printf("%d\n",cot-hungary()); return 0; }