【ACWing】406. 放置机器人

本文介绍了一种使用二分图和匈牙利算法解决激光机器人在地图上放置问题的方法。通过行、列连通性划分等价类,找到最大匹配来确定在不互相攻击的条件下最多可放置的机器人数量。每组数据的时间复杂度为O((mn)^2),空间复杂度为O(mn)。
摘要由CSDN通过智能技术生成

题目地址:

https://www.acwing.com/problem/content/description/408/

给出一个地图(网格),格子分为空地,草地,墙壁。要在空地上放能向上下左右 4 4 4个方向发射激光的机器人。墙壁能挡住激光,草地不能挡住激光也不能放机器人。在机器人不能互相打到对方的情况下,最多放置多少个机器人。

输入格式:
第一行包含整数 T T T,表示共有 T T T组测试数据。
每组数据第一行包含两个整数 m m m n n n,表示地图的大小为 m m m n n n列。
接下来 m m m行,每行包含 n n n个字符,用来描述整个地图。
#代表墙壁,*代表草地,o代表空地。

输出格式:
每组测试数据在第一行输出Case :idid是数据编号,从 1 1 1开始。

第二行包含一个整数,表示机器人的个数。

数据范围:
1 ≤ m , n ≤ 50 1≤m,n≤50 1m,n50

思路是二分图。将除了墙的地面定义一个等价关系叫行连通,两个非墙地面行连通当且仅当从一个能沿着行不经过墙走到另一个。那么这个等价关系可以将所有的非墙地面分为若干个等价类,我们把这些等价类编号为 1 , 2 , . . . , r 1,2,...,r 1,2,...,r;同理将列连通等价类编号为 1 , 2 , . . . , c 1,2,...,c 1,2,...,c。将 1 , 2 , . . . , r 1,2,...,r 1,2,...,r视为二分图左边的点, 1 , 2 , . . . , c 1,2,...,c 1,2,...,c视为二分图右边的点,如果某个行连通块和某个列连通块有交集,并且交集是o即空地,则将他们的编号之间连一条边。首先,这个二分图的任何一个匹配对应着一种放置方案:每条边对应一个空地,并且同一个行连通块里只能有一个机器人(因为会互相攻击),同一个列连通块也只能有一个机器人。其次,任意一个放置方案,对应一个匹配。所以最多的机器人放置方案一定就是二分图的最大匹配,从而可以用匈牙利算法来做。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 55, M = 2550;
int n, m, rcnt, ccnt;
char a[N][N];
int rid[N][N], cid[N][N];
int match[M];
bool vis[M];
int h[M], e[M], ne[M], idx;

void add(int a, int b) {
  e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool dfs(int u) {
  for (int i = h[u]; ~i; i = ne[i]) {
    int v = e[i];
    if (vis[v]) continue;
    vis[v] = true;
    if (!match[v] || dfs(match[v])) {
      match[v] = u;
      return true;
    }
  }
  return false;
}

int main() {
  int T;
  scanf("%d", &T);
  for (int t = 1; t <= T; t++) {
    memset(h, -1, sizeof h);
    idx = 0;
    memset(rid, 0, sizeof rid);
    memset(cid, 0, sizeof cid);
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= m; i++) scanf("%s", a[i] + 1);

    rcnt = 1, ccnt = 1;
    for (int i = 1; i <= m; i++)
      for (int j = 1; j <= n; j++) {
        if (a[i][j] != '#') {
          while (j <= n && a[i][j] != '#') rid[i][j++] = rcnt;
          rcnt++;
        }
      }

    for (int j = 1; j <= n; j++)
      for (int i = 1; i <= m; i++) {
        if (a[i][j] != '#') {
          while (i <= m && a[i][j] != '#') cid[i++][j] = ccnt;
          ccnt++;
        }
      }

    for (int i = 1; i <= n; ++i)
      for (int j = 1; j <= m; ++j)
        if (a[i][j] == 'o') add(rid[i][j], cid[i][j]);

    int res = 0;
    memset(match, 0, sizeof match);
    // 行连通块的个数为rcnt - 1
    for (int i = 1; i < rcnt; ++i) {
      memset(vis, 0, sizeof vis);
      if (dfs(i)) res++;
    }

    printf("Case :%d\n%d\n", t, res);
  }
}

每组数据时间复杂度 O ( ( m n ) 2 ) O((mn)^2) O((mn)2),空间 O ( m n ) O(mn) O(mn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值