二分图匹配(缩点建图)—— Fire Net ( HDU 1045 )

  • 题目链接:
    http://acm.hdu.edu.cn/showproblem.php?pid=1045

  • 分析:
    棋盘游戏的基础上(两点不能在同行同列),加了一个墙的概念,如果两个点之间有墙是可以在同一行/列的。所以就不能单纯地用行和列来建二分图进行匹配了,需要进行缩点重新建图。

  • 题解:
    读取输入的时候可以用getchar(),需要注意的是,每读取完一行需要用一个getchar()来拿走那个换行符。读取完毕后,进行缩点:先把相同的行区域缩成一个点,便利每一个点,如果该点是空的并且它的行区域没有标号,就从这点开始在同一行往后找,把所有连续的空点标上相同的序号,直到碰到墙或者结束,然后行区域序号+1.列区域的标号同样。缩点操作后开始建图,如果某点为空,那么它所属的行区域和列区域就可以连上一条边,以此遍历所有点,连上边。然后用匈牙利算法即可算出最大匹配。

  • AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#define N 6
using namespace std;

char Map[N][N];
int flag[N];

int row[N][N], col[N][N], r[N], c[N];
int cnt_col,cnt_row;
bool path[N][N];

int DFS(int x)
{
    for(int i=0;i<cnt_col;i++)
    {
        if(!path[x][i] || flag[i])//如果行区域x与列区域i之间没有边 或者再搜x的情况里已经被搜索过了,就跳过。
            continue;
        flag[i] = 1;
        if( c[i] == -1|| DFS(c[i]))//如果这个列区域i没有匹配或者它还能匹配到其它的行区域就更新匹配。
        {
            c[i] = x;
            r[x] = i;
            return 1;
        }
    }
    return 0;
}

void  Hungarian()
{
    int ans = 0;
    memset(r, -1, sizeof(r));
    memset(c, -1, sizeof(c));
    for(int i=0;i<cnt_row;i++)
    {
        if(r[i] == -1)//如果该行区域未匹配就用DFS搜
        {
            memset(flag, 0, sizeof(flag));
            ans+=DFS(i);
        }
    }
    cout << ans << endl;
}



int main()
{
    int n;
    while(scanf("%d%*c", &n), n)
    {
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n; ++j)
                Map[i][j] = getchar();
            getchar();
        }

        memset(row, -1, sizeof(row));
        memset(col, -1, sizeof(col));
        cnt_row = cnt_col = 0;
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                if(Map[i][j] == '.' && row[i][j] == -1)
                {           //横向缩点
                    for(int k = j; Map[i][k] == '.' && k < n; k++)
                        row[i][k] = cnt_row;  //给相同的行区域标记同一个数字
                    cnt_row++;
                }


                if(Map[j][i] == '.' && col[j][i] == -1)
                {           //纵向缩点
                    for(int k = j; Map[k][i] == '.' && k < n; k++)
                        col[k][i] = cnt_col;  //给相同的列区域标记同一个数字
                    cnt_col++;
                }
            }
        }

        memset(path, false, sizeof(path));
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < n; ++j)
                if(Map[i][j] == '.')    //连边,'.'的地方即为缩点后的 行和列的交点
                    path[ row[i][j] ][ col[i][j] ] = true;

        Hungarian();

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值