思路:

一个骨牌覆盖两个相邻的格子,也就是相邻的格子有边相连,不相邻的格子没有边相连,所以可以分成两个集合,是偶数的和是奇数的分开,恰好与“0要素”对应。每个格子只能被1张骨牌覆盖,恰好与“1要素”对应。
将没有被禁止的格子分成左右两个集合,建一个左集合节点到右集合节点的有向图(根据具体实现的原理可以不需要右集合节点到左集合节点的边)是一张二分图。
最多有多少条匹配边就是这题的结果
复杂度有点高:
经常重复敲的代码尽量写成函数,写错了容易找到问题,也美观。

MyCode:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+7,maxm=2e4+7;
bool used[maxn],vis[107][107];
int boy[maxn];
int head[maxn],Next[maxm],to[maxm],tot;
inline ll read() {
    ll s = 0, w = 1;
    char ch = getchar();
    while (ch < 48 || ch > 57) {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= 48 && ch <= 57)
        s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
void add(int x,int y) {
    to[++tot]=y;
    Next[tot]=head[x];
    head[x]=tot;
}
bool dfs(int x) {
    for(int i=head[x],y; i; i=Next[i]) {
        if(!used[ y=to[i] ]) {
            used[y]=true;
            if(!boy[y]||dfs(boy[y])) {
                boy[y]=x;
                return true;
            }
        }
    }
    return false;
}
int n,m,ans;
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
void link(int a,int b) {
    int num1=n*(a-1)+b,num2;
    for(int i=0,x,y;i<4;++i) {
        x=a+dis[i][0],y=b+dis[i][1];
        num2=n*(x-1)+y;
        if(vis[x][y]||x<1||y<1||x>n||y>n) continue;
        add(num1,num2);
    }
}
int main() {
    n=read(),m=read();
    while(m--)
        vis[read()][read()]=true;
    for(int i=1,j;i<=n;i+=2)
    for(j=1;j<=n;j+=2) if(!vis[i][j]) link(i,j);
    for(int i=2,j;i<=n;i+=2)
    for(j=2;j<=n;j+=2) if(!vis[i][j]) link(i,j);
    for(int i=1,j,num; i<=n; i+=2)
        for(j=1,num=n*(i-1)-1; j<=n; j+=2) {
            num+=2;
            memset(used,false,sizeof used);
            if(!vis[i][j]&&dfs(num)) ans+=1;
        }
    for(int i=2,j,num; i<=n; i+=2)
        for(j=2,num=n*(i-1); j<=n; j+=2) {
            num+=2;
            memset(used,false,sizeof used);
            if(!vis[i][j]&&dfs(num)) ans+=1;
        }
    printf("%d\n",ans);
}