洛谷P5030
发现这张图有点眼熟,与骑士共存问题问题很像。
按照骑士共存问题的解法,对于图黑白染色后进行连边求最小割即可。
源点向每个白点、每个黑点向汇点都连容量为 \(1\) 的边;
对于每个白点,从它向所能到达的黑点连容量无穷大的边。
这样跑出来的最大流也就是最小割,用总数减去最小割就是答案。
原理也比较简单,每个白点和它所连向的黑点肯定是不能同时放长脖子鹿的,所以赋值为无穷大是为了防止被删,而删去一条边是表示不选这个点。
但是做完发现并不对,这是为什么呢?
可以发现,在骑士共存问题这一题中,正常的行列奇偶性黑白染色可以完美地使每个白点到达的点都是黑点。
也就是说黑白染色的目的就是把点分为左右两部分,使得左边的点只向右边的点连边。
这一题中,由于长脖子鹿的攻击范围是 \(1\times 3\) 的格子,所以那种染色方法并不行。但是我们还可以发现如果只按行奇偶性染色,使每一行都是一种颜色,也满足上面的条件。
另外注意会有重点,所以不能直接使用 \(k\) 作为总数。
如此,问题便正式解决。
(采用了一种令人无法理解的加边方法,请见谅)
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 600010
#define maxm 70000000
#define INF 0x3f3f3f3f
//#define int long long
using namespace std;
bool vis[210][210];
int n,m,s,t,tot=1,all,k;
int val[210][210];
int id[210][210],now;
int Dis[maxn],cur[maxn],head[maxn];
struct edge{int fr,to,dis,nxt;}e[maxm];
int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
void add(int fr,int to,int dis){
e[++tot]=(edge){fr,to,dis,head[fr]};head[fr]=tot;
e[++tot]=(edge){to,fr,0,head[to]};head[to]=tot;
}
bool bfs(){
memset(Dis,-1,sizeof Dis);
queue<int> q;q.push(s);
Dis[s]=0;cur[s]=head[s];
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt){
int to=e[i].to;
if(Dis[to]==-1&&e[i].dis){
q.push(to);
Dis[to]=Dis[u]+1;
cur[to]=head[to];
if(to==t) return true;
}
}
}
return false;
}
int dfs(int u,int limit){
if(u==t) return limit;int flow=0;
for(int i=cur[u];i&&flow<limit;i=e[i].nxt){
int to=e[i].to;cur[u]=i;
if(Dis[to]==Dis[u]+1&&e[i].dis){
int f=dfs(to,min(e[i].dis,limit-flow));
if(!f)Dis[to]=-1;
e[i].dis-=f;e[i^1].dis+=f;
flow+=f;
}
}
return flow;
}
int dinic(){
int Maxflow=0,flow=0;
while(bfs())
while(flow=dfs(s,INF))
Maxflow+=flow;
return Maxflow;
}
bool judge2(int x,int y){
return (x>=1&&x<=n&&y>=1&&y<=m);
}
signed main(){
n=read();m=read();k=read();s=n*m+1;t=s+1;
for(int i=1,fr,to;i<=k;i++){fr=read();to=read();val[fr][to]=1;}
for(int i=1;i<=n;i+=2)for(int j=1;j<=m;j++)vis[i][j]=1;k=0;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) id[i][j]=++now;
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
if(val[i][j]) k++;
if(vis[i][j]&&!val[i][j]){
add(s,id[i][j],1);
if(!val[i-3][j-1]&&judge2(i-3,j-1)) add(id[i][j],id[i-3][j-1],INF);
if(!val[i-1][j-3]&&judge2(i-1,j-3)) add(id[i][j],id[i-1][j-3],INF);
if(!val[i+3][j-1]&&judge2(i+3,j-1)) add(id[i][j],id[i+3][j-1],INF);
if(!val[i+1][j-3]&&judge2(i+1,j-3)) add(id[i][j],id[i+1][j-3],INF);
if(!val[i-3][j+1]&&judge2(i-3,j+1)) add(id[i][j],id[i-3][j+1],INF);
if(!val[i-1][j+3]&&judge2(i-1,j+3)) add(id[i][j],id[i-1][j+3],INF);
if(!val[i+3][j+1]&&judge2(i+3,j+1)) add(id[i][j],id[i+3][j+1],INF);
if(!val[i+1][j+3]&&judge2(i+1,j+3)) add(id[i][j],id[i+1][j+3],INF);
}
else if(!val[i][j]){add(id[i][j],t,1);}
}
int ans=dinic();all=n*m;
printf("%d\n",all-k-ans);
return 0;
}