poj 2446 二分图匹配,匈牙利算法

本文探讨如何利用线段树解决棋盘覆盖问题,详细介绍了建图、染色、匹配等步骤,以及如何通过线段树进行优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给一个m*n的棋盘,然后挖掉p个格子,叫你用1*2的砖块来覆盖棋盘,问是否能完全覆盖。

好吧,,虽然知道是二分图,但是看到后在想线段树能不能做。。。。。原先有个贴小广告的线段树的题,不过忘了(QAQ捂脸)。

建图,首先,对于一个点来说,四个方向相邻的点都是可以匹配的。然后,用黑白点染色的方法把图分成两部分。嗯。

#include <stdio.h>
#include <string.h>
#define maxn 40
int vis[maxn*maxn],map[maxn*maxn][maxn*maxn],que[maxn*maxn],girl[maxn*maxn];
int n,m,p;
void add(int pos,int x)
{
    int i=pos/n;
    int j=pos%n;
    if(!j&&(x==1)) return;
    if(!j&&i==m&&(x==1||x==n)) return;
    if(!j&&i==1&&x==-n) return;
    if(!i&&j==1&&(x==-1||x==-n)) return;
    if(j==1&&(x==-1)) return;
    if(j==1&&(i==m-1)&&(x==-1||x==n)) return;
    if(pos+x<1||pos+x>m*n) return;
    if(que[pos+x]==-1) return;
    map[pos][pos+x]=1;
  //  printf("map[%d][%d]=%d\n",pos,pos+x,map[pos][pos+x]);
}
void match()
{
    int i,j,k;

    for(i=0;i<m;i++)
    {
        for(j=i%2;j<n;j+=2)
        {
            int tmp=i*n+j+1;
            if(que[tmp]==-1) continue;
            add(tmp,1);add(tmp,-1);add(tmp,n);add(tmp,-n);
        }
    }
}
int find(int pos)
{
    int i,j,k;
    for(i=1;i<=m*n;i++)
    {
        if(map[pos][i]==1&&!vis[i])
        {
            vis[i]=1;
            if(!girl[i]||find(girl[i]))
            {
                girl[i]=pos; //printf("girl[%d]=%d\n",i,pos);
                return 1;
            }
        }
    }
    return 0;
}

int work()
{
    int i,j,k;
    for(i=0;i<p;i++)
    {
        scanf("%d%d",&j,&k);
        que[(k-1)*n+j]=-1;
       // printf("que[%d]=%d\n",(k-1)*n+j,que[(k-1)*n+j]);
    }
    match();
    //print();
    int ret=0;
    for(i=0;i<m;i++)
    {
        for(j=i%2;j<n;j+=2)
        {
            k=i*n+j+1;
            if(que[k]==-1) continue;
            memset(vis,0,sizeof(vis));
            if(!find(k)) return 0;
        }
    }
    return 1;
}
void init()
{
    memset(girl,0,sizeof(girl));
    memset(map,0,sizeof(map));
    memset(que,0,sizeof(que));
}
int main()
{
    while(scanf("%d%d%d",&m,&n,&p)!=EOF)
    {
        int i,j,k;
        init();
        if(work()) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
/*
4 3 2
2 1
3 3
4 4 4
3 3
3 2
2 2
2 3
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值