匈牙利算法 二分图

  这几个图和定义转自https://www.byvoid.com/blog/hungary/

 未盖点:设Vi是图G的一个顶点,如果Vi 不与任意一条属于匹配M的边相关联,就称Vi 是一个未盖点。

交错路:设P是图G的一条路,如果P的任意两条相邻的边一定是一条属于M而另一条不属于M,就称P是一条交错路。

可增广路:两个端点都是未盖点的交错路叫做可增广路。

 

  匈牙利算法是用来求最大匹配数的,主要就是找增广路,增广路其实就是虚边-实边-虚边-实边...-虚边这个样子,第一条和最后一条都是虚的,实边连的两个点就是配对的,找增广路就是再找一个不在M中的点,看能不能把路变长或者找到一条匹配的新边。

  有两组x,y,用cx,cy数组分别记录x组的节点匹配情况和y组节点匹配情况(cx[i]=j就是x组的第i个点和y组的第j个点匹配,如果没匹配为0)

int augment_path(int u)
{
    int v;
    for(v=1; v<=q; v++)
        if(edge[u][v]&&!vis[v])
        {
            vis[v]=1;
            if(!cy[v]||augment_path(cy[v])) //如果v没匹配或者从v本来对应的x组中的点找到了增广路,u就可以和v匹配
            {
                cx[u]=v;
                cy[v]=u;
                return 1;
            }
        }
    return 0;
}
void hungary()
{
    memset(cx,0,sizeof(cx));
    memset(cy,0,sizeof(cy));
    int i;
    for(i=1; i<=p; i++)
        if(!cx[i])  //如果有未匹配的节点,看能不能从这个点开始找到增广路
        {
            memset(vis,0,sizeof(vis));
            if(augment_path(i)) ans++;
        }
}


A - Chessboard
Time Limit:2000MS    Memory Limit:65536KB    64bit IO Format:%I64d & %I64u

Alice and Bob often play games on chessboard. One day, Alice draws a board with size M * N. She wants Bob to use a lot of cards with size 1 * 2 to cover the board. However, she thinks it too easy to bob, so she makes some holes on the board (as shown in the figure below).

We call a grid, which doesn’t contain a hole, a normal grid. Bob has to follow the rules below:
1. Any normal grid should be covered with exactly one card.
2. One card should cover exactly 2 normal adjacent grids.

Some examples are given in the figures below:

A VALID solution.


An invalid solution, because the hole of red color is covered with a card.


An invalid solution, because there exists a grid, which is not covered.

Your task is to help Bob to decide whether or not the chessboard can be covered according to the rules above.

Input

There are 3 integers in the first line: m, n, k (0 < m, n <= 32, 0 <= K < m * n), the number of rows, column and holes. In the next k lines, there is a pair of integers (x, y) in each line, which represents a hole in the y-th row, the x-th column.

Output

If the board can be covered, output "YES". Otherwise, output "NO".

Sample Input

4 3 2
2 1
3 3

Sample Output

YES

  这种棋盘覆盖问题就可以转化为二分图问题了,把棋盘上的相邻的点分为两组,求最大匹配数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#define INF 0x3f3f3f3f
int M,N,K;
int ans,p,q;
int a[50][50],vis[1000],map[50][50],cx[1000],cy[1000],edge[1000][1000];
int move[4][2]= {-1,0,0,-1,0,1,1,0};
int augment_path(int u)
{
    int v;
    for(v=1; v<=q; v++)
        if(edge[u][v]&&!vis[v])
        {
            vis[v]=1;
            if(!cy[v]||augment_path(cy[v]))
            {
                cx[u]=v;
                cy[v]=u;
                return 1;
            }
        }
    return 0;
}
void hungary()
{
    memset(cx,0,sizeof(cx));
    memset(cy,0,sizeof(cy));
    int i;
    for(i=1; i<=p; i++)
        if(!cx[i])  
        {
            memset(vis,0,sizeof(vis));
            if(augment_path(i)) ans++;
        }
}
int main()
{
    while(scanf("%d%d%d",&M,&N,&K)!=EOF)
    {
        int i,j,k,m,n,x,y;
        ans=0;
        p=0;
        q=0;
        memset(map,0,sizeof(map));
        memset(a,0,sizeof(a));
        for(i=1; i<=K; i++)
        {
            scanf("%d%d",&m,&n);
            map[n][m]=1;
        }
        for(i=1; i<=M; i++)
            for(j=1; j<=N; j++)
            {
                if((i+j)%2==0) a[i][j]=++p;
                else a[i][j]=++q;
            }
        for(i=1; i<=M; i++)
            for(j=1; j<=N; j++)
                if((i+j)%2==0&&!map[i][j])
                    for(k=0; k<4; k++)
                    {
                        x=i+move[k][0];
                        y=j+move[k][1];
                        if(x>0&&x<=M&&y>0&&y<=N&&!map[x][y])
                        {
                            edge[a[i][j]][a[x][y]]=1;
                        }
                    }
        hungary();
        if(ans*2+K==M*N)
            printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值