ZOJ1654 Place the Robots

一.原题链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1654

二.题目大意:给一个矩阵,每个格子里面有#,*,和o,分别代表墙,玻璃和空地,空地上可以放机器人,机器人的激光可以穿透玻璃不能穿透墙,激光可以横着走或竖着走,求最多可以放多少个机器人。

三.思路:乍一看,八皇后问题,直接想深搜,一格一格搜,就是求最大独立点集,但是为什么会出现在二分图匹配上呢?看了一下数据规模,50,2的50次方复杂度(233333...)

接下来说一下二分匹配建图思路:

1.将每一行相连的几个格子看作一个点,标号。记录在xs[i][j]上,空地标号,墙和玻璃标号为0。

2.将每一列相连的几个格子看作一个点,标号。记录在ys[i][j]上,如上。

3.扫一次xs[i][j],如果大于0,说明这是空地,连接xs[i][j]与ys[i][j],表示这一块空地被标号为xs[i][j]的行(点)和标号为ys[i][j]的列(点)占用(ys[i][j]肯定也会大于0)。当然会出现连多次,但这是不带权的没关系。

建图过程就完了,求得的最大匹配数,就是机器人可以放置数。

概念扫盲:

匹配:就是独立边集,任意2条边不相邻。

最大匹配:最大独立边集。

求出来的表示行和列都被占用的块,只能填一个机器人。

二分图最大匹配算法:匈牙利算法

二分图点集被分为X和Y,扫描X中的每一个x,没有匹配的话,就在Y中去找一个没有被匹配的y。

1.找不到和x相连的,在X中继续找下一个。

2.找到和x相连的,但是被匹配了,让那个已经被匹配的x'去找别的y匹配,当前x占它的位置。

四.代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <cstdlib>

using namespace std;

const int INF = 0x3f3f3f3f,
          MAX_SIZE = 58;

class maxMatch
{
public:
    bool weight[MAX_SIZE*MAX_SIZE][MAX_SIZE*MAX_SIZE],
         visited[MAX_SIZE*MAX_SIZE];
    int xMatch[MAX_SIZE*MAX_SIZE],
        yMatch[MAX_SIZE*MAX_SIZE],
        numX, numY;

    void init()
    {
        numX = 0, numY = 0;
        memset(weight, 0, sizeof(weight));
    }


    bool path(int u)
    {
        int v;
        for(v = 1; v <= numY; v++)
        if(weight[u][v] && !visited[v]){
            visited[v] = true;
            if(-1 == yMatch[v] || path(yMatch[v])){
                yMatch[v] = u;
                xMatch[u] = v;
                return true;
            }
        }
        return false;
    }

    int getRes()
    {
        int i, j, res;

        memset(xMatch, -1, sizeof(xMatch));
        memset(yMatch, -1, sizeof(yMatch));

        res = 0;
        for(i = 1; i <= numX; i++)
            if(-1 == xMatch[i]){
                memset(visited, 0, sizeof(visited));
                if(path(i))
                    res++;
            }

        return res;
    }
}G;

int xs[MAX_SIZE][MAX_SIZE],
    ys[MAX_SIZE][MAX_SIZE],
    row, col;
char mp[MAX_SIZE][MAX_SIZE];

void buildG()
{
    int i, j, num = 0;
    bool flag;
    for(i = 0; i < row; i++){
        flag = false;
        for(j = 0; j < col; j++)
        if('o' == mp[i][j]){
            if(!flag) num++;
            xs[i][j] = num;
            flag = true;
        }
        else{
            xs[i][j] = 0;
            if('#' == mp[i][j])
                flag = false;
        }
    }

    G.numX = num;
    num = 0;
    for(j = 0; j < col; j++){
        flag = false;
        for(i = 0; i < row; i++)
        if('o' == mp[i][j]){
            if(!flag) num++;
            ys[i][j] = num;
            flag = true;
        }
        else{
            ys[i][j] = 0;
            if('#' == mp[i][j])
                flag = false;
        }
    }
    G.numY = num;
    for(i = 0; i < row; i++)
        for(j = 0; j < col; j++)
        if(xs[i][j])
            G.weight[xs[i][j]][ys[i][j]] = true;
}

int main()
{
    //freopen("in.txt", "r", stdin);
    int test, i, kase = 1;
    scanf("%d", &test);
    while(test--){
        G.init();
        scanf("%d%d", &row, &col);

        for(i = 0; i < row; i++)
            scanf("%s", mp[i]);

        buildG();
        printf("Case :%d\n%d\n", kase++, G.getRes());
    }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值