uva 11419 SAM I AM (最小覆盖 König定理)

来源:互联网 发布:使命召唤7优化差 编辑:程序博客网 时间:2024/05/10 00:46

uva 11419 SAM I AM

题目大意:给出一个R×C的网格,网格上棉纺了一些目标。可以在网格外发射子弹,子弹会沿着垂直或水平方向飞行,并且打掉飞行路径上的所有目标。你的任务是计算出最少需要多少子弹,各从哪个位置发射,才能把所有目标全部打掉。

解题思路:König定理:最小覆盖数等于最大匹配数。把目标所在的坐标,转化为XY结点,行看成X结点,列看成Y结点。那现在问题就变成了,如何选最少的结点,覆盖所有的边。

求最小覆盖的步骤大致如下:1)在右边找到一个未被匹配过的点,标记。2)走一条没被匹配过的边,到左边的点,标记。3)走一条匹配过的边到右边,标记。4)重复2,3步骤直到不能再走。5)回到步骤一,直到找不到未被匹配且未被标记的右边的点。6)标记结束后,右边没有标记的点,和左边标记过的点,就可以覆盖所有的边。

想理解的更透彻,可以看这里。

#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <cstdlib>#include <queue>using namespace std;const int N = 1005;typedef long long ll;int n, m, k;vector<int> X, Y;void init() {    X.clear();    Y.clear();}struct BPM{      int n, m;      vector<int > G[N];      int left[N];      int right[N];      bool T[N];      bool S[N];      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; //标记右边的点u        for(int i = 0; i < G[u].size(); i++){ //遍历由u点出发,连接的左边的点             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++){              memset(S, 0, sizeof(S));              memset(T, 0, sizeof(T));              if(match(u)) ans++;  //先用匈牙利算法求出最大匹配        }          return ans;      }      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); //从这个未标记的点开始走增广路         for(int u = 0; u < n; u++)              if(!S[u]) X.push_back(u); //标记结束之后,记录右边没标记的点         for(int v = 0; v < n; v++)              if(T[v]) Y.push_back(v);  //记录左边标记过的点        return ans;      }  }bpm;  void input() {    int x, y;    for (int i = 0; i < k; i++) {        scanf("%d %d", &x, &y);         x--, y--;        bpm.addEdge(x, y);    }}int main() {    while (scanf("%d %d %d", &n, &m, &k) == 3)  {        if (!n && !m && !k) break;        bpm.init(n, m);                 init();        input();        printf("%d", bpm.mincover(X, Y));        for (int i = 0; i < X.size(); i++) printf(" r%d", X[i] + 1);        for (int i = 0; i < Y.size(); i++) printf(" c%d", Y[i] + 1);        puts("");    }    return 0;}
0 0
原创粉丝点击