POJ2446——匈牙利算法讲解

POJ2446——chessboard(匈牙利算法) 二分图最大匹配数。

今天第一次写博客,给大家讲讲一个比较著名的二分图算法——匈牙利算法。

先来普及一下二分图的概念,

POJ2446——匈牙利算法讲解_第1张图片
如图中有两个集合,U和V。只要U、V同一集合内点不直接相交(可以间接相连)就属于二分图。

匹配:

一条边的两个端点分别在两个集合里,这条边就是匹配边。最大匹配数图中最多能存在的没有公共端点的匹配边的数目
POJ2446——匈牙利算法讲解_第2张图片
上图最大匹配数是4,U集合有点没连上也没办法,已经没有空的V点了。

增广路

POJ2446——匈牙利算法讲解_第3张图片
假设只确定了一条匹配边(红色的),那么两条蓝色和红色的组成一条增广路,增广路的定义是:起点和终点是非匹配点,路径由匹配边与非匹配边交替。 显然,一条增广路的非匹配边比匹配边多1条,我们将蓝色的当作匹配边,红色当作非匹配边,这四个点的匹配边数就可以加1。 匈牙利算法的原理就是通过找增广路径来更新最大匹配边数。

具体匈牙利算法模拟可以看下面链接。

嗯,里面用男女关系来讲解确实简单易懂
趣味算法系列之匈牙利算法

下面讲题

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).
POJ2446——匈牙利算法讲解_第4张图片

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:
POJ2446——匈牙利算法讲解_第5张图片
A VALID solution
POJ2446——匈牙利算法讲解_第6张图片
POJ2446——匈牙利算法讲解_第7张图片
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

题意是要用多条纸带覆盖一幅图的格子,因为一条纸带只能覆盖两个相邻的格子,我们将有洞的排除掉,将好的分成奇数和偶数号格子(我这里是根据横纵坐标和的奇偶性来分)。红色是奇数格子的编号,蓝色是偶数格子的编号。
POJ2446——匈牙利算法讲解_第8张图片

把奇数格子和偶数格子当成二分图的两个集合求最大匹配边即可。如果最大匹配边数乘2和剩下格子数相等,就可以全覆盖。如果剩下格子为单数必定不能全覆盖。

#include
#include
#include
using namespace std;
const int M=35*35;
bool used[M];     //记录偶数格子是否被使用;
int dan[M];        //记录偶数格子和哪个单数格子配对;
bool match[M][M];
int map1[35][35];
int v1,v2;      // v1单数格子数,v2偶数格子数;
bool dfs(int x)
{
    for(int i=1;iif(match[x][i]&&!used[i])
        {
            used[i]=true;
            if(dan[i]==0||dfs(dan[i]))
            {
                dan[i]=x;
                return 1;
            }
        }
    }
    return 0;
}
int seek(int n)
{
    memset(dan,0,sizeof(dan));
    int ans=0;
    for(int i=1;imemset(used,0,sizeof(used));
        if(dfs(i))
            ans+=1;
    }
    return ans;
}
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&m,&n,&k))
    {
        int x1,y1;
        memset(map1,0,sizeof(map1));
        for(int i=0;iscanf("%d%d",&x1,&y1);
            map1[y1][x1]=-1;           //题目的陷阱
         }
        if ((n*m - k) % 2 != 0)
        {
            printf("NO\n");
            continue;
        }
        v1=v2=1;
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(map1[i][j]==0)
                {
                    if((i+j)%2==0)
                    {
                        map1[i][j]=v2++;
                    }
                    else
                    {
                        map1[i][j]=v1++;
                    }
                }
            }
        }
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if((i+j)%2==0||map1[i][j]==-1)
                {
                    continue;
                }
                if(i-1>0&&map1[i-1][j]>0)
                {
                    match[map1[i][j]][map1[i-1][j]]=true;
                }
                if(j-1>0&&map1[i][j-1]>0)
                {
                    match[map1[i][j]][map1[i][j-1]]=true;
                }
                if(i+1<=m&&map1[i+1][j]>0)
                {
                    match[map1[i][j]][map1[i+1][j]]=true;
                }
                if(j+1<=n&&map1[i][j+1]>0)
                {
                    match[map1[i][j]][map1[i][j+1]]=true;
                }
            }
        }
        int cnt=seek(v1);
        if(cnt==(n*m-k)/2)
            printf("YES\n");
        else
            printf("NO\n");
    }
}

第一次编写博客可能写的不太好,这markdown编辑器还不太会用,而且换行的时候上文还经常自己打乱,心都烦。以后可能没周更一篇,欢迎大家浏览。。我还只是个acm新手,如果讲的不够清楚,大家多提意见哈。

你可能感兴趣的:(acm,poj)