Sentry Robots UVA - 12549 (二分图最小覆盖+图的转化)

传送门

题意:在一个Y行X列的网格里有空顶.,重要位置*和障碍物#,用最少的机器人看守所有重要位置。每个机器人放在一个格子里,面朝上下左右四个方向,机器人会发出激光,一直碰到障碍物为止,机器人不会阻挡射线,不同的机器人不能放在同一个格子。

题解:这道题是传送门这道题的发展版,如果说把这道题的障碍物转化为不带障碍物的模型就好了,如何进行转化,从上到下,从左到右遍历每个点,遇到障碍时,就将障碍以及所有右边的点(包括障碍物和重要位置)向下移动一行,这样将左边的点和右边的点分割成上下两行,同理,按行进行转化之后,继续按照列进行类似的转化。

附上代码:


#include<bits/stdc++.h>

using namespace std;

const int MAXX=1e2+5;

// 二分图最大基数匹配
template<int maxn>
struct BPM {
  int n, m;               // 左右顶点个数
  vector<int> G[maxn];    // 邻接表
  int left[maxn];         // left[i]为右边第i个点的匹配点编号,-1表示不存在
  bool T[maxn];           // T[i]为右边第i个点是否已标记

  int right[maxn];        // 求最小覆盖用
  bool S[maxn];           // 求最小覆盖用

  void init(int n, int m) {
    this->n = n;
    this->m = m;
    for(int i = 0; i < n; i++) G[i].clear();
  }

  void AddEdge(int u, int v) {
    G[u].push_back(v);
  }

  bool match(int u){
    S[u] = true;
    for(int i = 0; i < G[u].size(); i++) {
      int v = G[u][i];
      if (!T[v]){
        T[v] = true;
        if (left[v] == -1 || match(left[v])){
          left[v] = u;
          right[u] = v;
          return true;
        }
      }
    }
    return false;
  }

  // 求最大匹配
  int solve() {
    memset(left, -1, sizeof(left));
    memset(right, -1, sizeof(right));
    int ans = 0;
    for(int u = 0; u < n; u++) { // 从左边结点u开始增广
      memset(S, 0, sizeof(S));
      memset(T, 0, sizeof(T));
      if(match(u)) ans++;
    }
    return ans;
  }

  // 求最小覆盖。X和Y为最小覆盖中的点集
  int mincover(vector<int>& X, vector<int>& Y) {
    int ans = solve();
    memset(S, 0, sizeof(S));
    memset(T, 0, sizeof(T));
    for(int u = 0; u < n; u++)
      if(right[u] == -1) match(u); // 从所有X未盖点出发增广
    for(int u = 0; u < n; u++)
      if(!S[u]) X.push_back(u); // X中的未标记点
    for(int v = 0; v < m; v++)
      if(T[v]) Y.push_back(v);  // Y中的已标记点
   return ans;
  }
};

struct Point{
    int x,y;
    Point(){}
    Point(int _x,int _y):x(_x),y(_y){}
    char ch;
};

bool cmp(Point p1,Point p2)
{
    if(p1.y!=p2.y){
        return p1.y<p2.y;
    }
    return p1.x<p2.x;
}

bool cmp1(Point p1,Point p2)
{
    if(p1.x!=p2.x){
        return p1.x<p2.x;
    }
    return p1.y<p2.y;
}

int Y,X,P,W;
vector<Point>points;

BPM<MAXX*MAXX*2>solver;

int solve()
{
    sort(points.begin(),points.end(),cmp);
    int dy=0;
    for(auto&p:points){
        bool isob=(p.ch=='#');
        if(isob){
            dy++;
        }
        p.y+=dy;
        if(isob){
            dy++;
        }
    }
    Y+=dy;
    sort(points.begin(),points.end(),cmp1);
    int dx=0;
    for(auto&p:points){
        bool isob=(p.ch=='#');
        if(isob){
            dx++;
        }
        p.x+=dx;
        if(isob){
            dx++;
        }
    }
    X+=dx;
    solver.init(X,Y);
    for(auto&p:points){
        if(p.ch=='*'){
            solver.AddEdge(p.x,p.y);
        }
    }
    vector<int>t1,t2;
    solver.mincover(t1,t2);
    return t1.size()+t2.size();
}

int main()
{
    int c;
    cin>>c;
    for(int t=1;t<=c;t++){
        cin>>Y>>X>>P;
        points.clear();
        Point p;
        for(int i=0;i<P;i++){
            cin>>p.y>>p.x;
            p.ch='*',p.x--,p.y--;
            points.push_back(p);
        }
        cin>>W;
        for(int i=0;i<W;i++){
            cin>>p.y>>p.x;
            p.ch='#',p.x--,p.y--;
            points.push_back(p);
        }
        int ans=solve();
        cout<<ans<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值