P3355 騎士共存問題
題目描述
在一個 n*n (n <= 200)個方格的國際象棋棋盤上,馬(騎士)可以攻擊的棋盤方格如圖所示。棋盤上某些方格設置了障礙,騎士不得進入

對於給定的 n*n 個方格的國際象棋棋盤和障礙標志,計算棋盤上最多可以放置多少個騎士,使得它們彼此互不攻擊
Solution
二分圖最大獨立集
騎士共存是這個的經典模型
兩個點互相干涉的點只能取其一
定理: 二分圖的最大獨立集為其點數減去最大匹配數
證明:
最大獨立集: 最多互不干涉的點
\(\Rightarrow\) 選出最少的點使得剩下的互不干涉
\(\Rightarrow\) 選出最多的點覆蓋所有干涉邊
而最小點覆蓋 \(=\) 最大匹配數
故成立
證畢。
類似棋盤覆蓋問題, 我們將棋盤黑白染色
發現此點與干涉點屬於不同的顏色
故有干涉關系的連邊做二分圖最大匹配即可
此題卡匈牙利算法, 使用最大流
Code
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define LL long long
#define REP(i, x, y) for(int i = (x);i <= (y);i++)
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 419, maxv = 1000019, INF = 1e9 + 19;
int head[maxn * maxn],nume = 1;
struct Node{
int v,dis,nxt;
}E[maxv << 3];
void add(int u,int v,int dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
int len, num;
int map[maxn][maxn];
int mx[8] = {-2,-1, 1, 2, 2, 1,-1,-2};
int my[8] = {-1,-2,-2,-1, 1, 2, 2, 1};
bool judge(int x, int y){
if(x < 1 || x > len || y < 1 || y > len)return 0;
return 1;
}
int id(int x, int y){return (x - 1) * len + y;}
int s, t, maxflow;
int d[maxn * maxn];
bool bfs(){
queue<int>Q;
memset(d, 0, sizeof(d));
d[s] = 1;
Q.push(s);
while(!Q.empty()){
int u = Q.front();Q.pop();
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(!d[v] && E[i].dis){
d[v] = d[u] + 1;
Q.push(v);
if(v == t)return 1;
}
}
}
return 0;
}
int Dinic(int u, int flow){
if(u == t)return flow;
int rest = flow, k;
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(d[v] == d[u] + 1 && E[i].dis){
k = Dinic(v, min(rest, E[i].dis));
if(!k)d[v] = 0;
E[i].dis -= k;
E[i ^ 1].dis += k;
rest -= k;
if(!rest)break;
}
}
return flow - rest;
}
int main(){
len = RD(), num = RD();
s = 0, t = maxn * maxn - 19;
REP(i, 1, num){
int x = RD(), y = RD();
map[x][y] = 1;
}
REP(i, 1, len)REP(j ,1, len){
if(map[i][j])continue;
int now = id(i, j);
if((i + j) % 2 == 1)add(s, now, 1), add(now, s, 0);
else add(now, t, 1), add(t, now, 0);
}
REP(i, 1, len)REP(j ,1, len){
if(map[i][j] || (i + j) % 2 == 0)continue;
int u = id(i ,j);
for(int k = 0;k < 8;k++){
int nx = i + mx[k];
int ny = j + my[k];
if(!judge(nx, ny))continue;
if(map[nx][ny])continue;
int v = id(nx, ny);
add(u, v, 1), add(v, u, 0);
}
}
int flow = 0;
while(bfs())while(flow = Dinic(s, INF))maxflow += flow;
printf("%d\n",len * len - maxflow - num);
return 0;
}
