匈牙利算法(二分图最大匹配) - 棋盘覆盖 - AcWing 372

匈牙利算法(二分图最大匹配) - 棋盘覆盖 - AcWing 372

给定一个N行N列的棋盘,已知某些格子禁止放置。

求最多能往棋盘上放多少块的长度为2、宽度为1的骨牌,骨牌的边界与格线重合(骨牌占用两个格子),并且任意两张骨牌都不重叠。

输入格式

第一行包含两个整数N和t,其中t为禁止放置的格子的数量。

接下来t行每行包含两个整数x和y,表示位于第x行第y列的格子禁止放置,行列数从1开始。

输出格式

输出一个整数,表示结果。

数据范围

1 ≤ N ≤ 100 1≤N≤100 1N100

输出样例:

8 0

输出样例:

32

分析:

为什么可以用匈牙利算法?

将 方 阵 从 左 到 右 , 从 上 到 下 依 次 染 色 : 将方阵从左到右,从上到下依次染色:

在这里插入图片描述
我 们 发 现 , 每 个 点 与 其 相 邻 点 都 是 相 异 的 。 我们发现,每个点与其相邻点都是相异的。

所 以 , 若 在 某 个 1 × 2 的 方 格 中 放 一 块 骨 牌 , 就 在 这 两 个 方 格 点 之 间 连 接 一 条 边 。 所以,若在某个1×2的方格中放一块骨牌,就在这两个方格点之间连接一条边。 1×2

我 们 的 目 标 是 : 在 指 定 的 部 分 方 格 内 , 放 尽 量 多 的 骨 牌 , 我们的目标是:在指定的部分方格内,放尽量多的骨牌,

即 : 在 给 定 的 部 分 点 中 , 连 接 尽 量 多 的 边 。 即:在给定的部分点中,连接尽量多的边。

我 们 发 现 , 这 是 一 个 二 分 图 的 最 大 匹 配 问 题 。 我们发现,这是一个二分图的最大匹配问题。

注意:

在 二 分 图 问 题 中 , 通 常 我 们 讨 论 的 是 无 向 边 。 在二分图问题中,通常我们讨论的是无向边。

但 是 , 由 于 点 分 成 了 两 类 , 我 们 每 次 考 虑 一 类 的 点 与 另 一 类 的 点 匹 配 , 但是,由于点分成了两类,我们每次考虑一类的点与另一类的点匹配,

故 在 建 图 的 过 程 中 , 仅 需 连 接 一 个 方 向 。 故在建图的过程中,仅需连接一个方向。

点 的 分 类 : 将 坐 标 和 为 奇 数 的 点 分 为 一 类 , 坐 标 和 为 偶 数 的 点 分 为 一 类 。 点的分类:将坐标和为奇数的点分为一类,坐标和为偶数的点分为一类。

代码:

#include<iostream>
#include<cstring>
#include<algorithm>

#define P pair<int,int>
#define x first
#define y second

using namespace std;

const int N=110;

int n,m;
int g[N][N],st[N][N];
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
P match[N][N];

bool Find(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        int a=x+dir[i][0],b=y+dir[i][1];
        if(a<1||a>n||b<1||b>n) continue;
        if(g[a][b] || st[a][b]) continue;
        
        st[a][b]=true;
        P t=match[a][b];
        if(t.x==-1 || Find(t.x,t.y))
        {
            match[a][b]={x,y};
            return true;
        }
    }
    
    return false;
}

int main()
{
    cin>>n>>m;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        g[a][b]=true;
    }
    
    memset(match,-1,sizeof match);
    int res=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if((i+j)%2 && !g[i][j])
            {
                memset(st,false,sizeof st); //搜过的点下次可以再搜,改变它的匹配
                if(Find(i,j)) res++;
            }
                
    cout<<res<<endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值