铺地板

题意

NS 中学新建了一个 n × n 的正方形广场,现在需要你用 1 × 2 的地板砖去铺这个广场。由于各种原因,广场上有些地方不能铺地板(比如那里种了一棵树或者有一个井盖)。显然,地板也不能重叠放置. 现在你需要求出最少有多少个格子不能被地板铺上(包括不能铺地板的地方)。

  • 看出二分图
  • 然后 $AC$ ~(滑稽)~

只要看出是二分图模型就非常简单
建双向边得出的$ans$最大匹配的二倍
但因为一条边连接两个点所以直接输出$n^2-ans$

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=70;
int n,head[maxn*maxn],num,p[maxn*maxn];
int cnt,t,map[maxn][maxn];
bool use[maxn*maxn];
int mx[4]={0,0,-1,1};
int my[4]={1,-1,0,0};
char str[maxn];
struct fy{int to,next;}q[maxn*maxn*10];
void add(int a,int b){q[++num]=(fy){b,head[a]};head[a]=num;}
bool find(int a)
{
    for(int i=head[a];i;i=q[i].next)
    {
        int b=q[i].to;
        if(!use[b])
        {
            use[b]=true;
            if(!p[b]||find(p[b]))
            {
                p[b]=a;
                return true;
            }
        }
    }
    return false;
}
int main()
{
    freopen("floor.in","r",stdin);
    freopen("floor.out","w",stdout);
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);memset(head,0,sizeof head);
        cnt=0;num=0;memset(map,0,sizeof map);
        memset(p,0,sizeof p);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",str);
            for(int j=0;j<n;j++)
            if(str[j]=='.')map[i][j+1]=++cnt;
        }
        for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
        if(map[i][j])
        {
            int a=map[i][j];
            for(int k=0;k<4;k++)
            {
                int b=map[i+mx[k]][j+my[k]];
                if(b)add(a,b);
            }
        }
        int ans=0;
        for(int i=1;i<=cnt;i++)
        {
            memset(use,false,sizeof use);
            if(find(i))ans++;
        }
        printf("%d\n",n*n-ans);
    }
    return 0;
}