• 【ACWing】406. 放置机器人


    题目地址:

    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 
    #include 
    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);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

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

  • 相关阅读:
    【ESD专题】TVS管的参数详解
    Linux:工具(vim,gcc/g++,make/Makefile,yum,git,gdb)
    springboot整合swagger3和knife4j
    linux系统学习笔记9——CANOpen状态转换
    Python数据挖掘—爬虫基础
    嵌入式分享合集43
    socket编程详解(二)——客户端
    uniapp 封装进度条组件
    C++ 课本习题(程序设计题)
    [激光原理与应用-39]:《光电检测技术-6》- 光干涉的原理与基础
  • 原文地址:https://blog.csdn.net/qq_46105170/article/details/126093036