POJ 2446 Chessboard (浅dfs+二维化一维+二分图匹配)

Chessboard - POJ 2446 - Virtual Judge (csgrandeur.cn)

要不是在做专项,头一次看到这个题完全想不到是二分图匹配的做法,看了大佬的博客发现二分图匹配很多时候是需要自己去构造或者是抽象出来的,就比如这道题,它完全就是靠题目条件“用1*2的卡片去覆盖“来抽象出来的一个二分图。其二分关系就是一点与周围相邻的点可以组合用一块卡片覆盖,就相当于是在传统二分图中的路径。

由dfs将相邻的不是洞的点遍历一遍建立路径关系。

跑一边朴素的匈牙利算法即可。需要注意,因为是点与点的对应关系,而点又是二维,所以要想和传统二分图匹配保持一致,就需要进行二维变一维的转化。

最后题目需要判断是否是完全匹配,即除了洞之外的所有点都要被覆盖,即(n*m-k)/2个卡片,但是根据匈牙利算法的原理,加上我们建的图是无向图,所以做后得出的匹配数是卡片数的二倍(可以联想成一张卡片头尾互换)。故如果最后的匹配数等于n*m-k就YES,否则就NO。

(小坑,输入的坐标是(y,x),纵坐标在前的。)

代码如下:

//#include<bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
//
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f
#define ll long long
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
int hole[40][40];
int mp[2005][2005];//需要开到转化后的范围
int vis[2005];
int match[2005];
int n,m,k;

int js(int x,int y)//二维化一维,将横坐标乘上纵坐标的总数加上纵坐标,这样可以保证一对一转化
{
  return (x-1)*m+y;
}
void dfs(int x,int y)//建立路径关系
{
  for(int i=0;i<4;i++)
  {
    int xx=x+dx[i],yy=y+dy[i];
    if(xx>=1&&yy>=1&&xx<=n&&yy<=m&&!hole[xx][yy])
    mp[js(x,y)][js(xx,yy)]=1;
  }
}
bool find(int x)//朴素的匈牙利算法
{
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=m;j++)
    {
      if(mp[x][js(i,j)]&&!vis[js(i,j)])
      {
        vis[js(i,j)]=1;
        if(!match[js(i,j)]||find(match[js(i,j)]))
        {
          match[js(i,j)]=x;
          return true;
        }
      }
    }
  }
  return false;
}
int main()
{
  IOS;
  
  cin>>n>>m>>k;
  for(int i=1;i<=k;i++)
  {
    int x,y;
    cin>>x>>y;
    hole[y][x]=1;
  }
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=m;j++)
    {
      if(!hole[i][j])
      dfs(i,j);
    }
  }
  int ans=0;
  for(int i=1;i<=n;i++)
  {
    for(int j=1;j<=m;j++)
    {
      memset(vis,0,sizeof vis);
      if(find(js(i,j)))
      ans++;
    }
  }
  if(ans==n*m-k)
  cout<<"YES"<<endl;
  else
  cout<<"NO"<<endl;
  return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值