UVa12549 Sentry Robots (二分图最大匹配,最小点集覆盖)

题意:http://vjudge.net/problem/UVA-12549

分析:

一个重要位置有(x,y)两个坐标,而要守住这个重要位置就相当于连一条x到y的弧。选了一个重要位置(x,y)放置机器人相当于选了所有x相同的弧或者y相同的弧。当所有的x或者y被选完的时候就完成了看守。具体来说二分图两列分别表示用编号代表行和列,从源点s向行连一条容量为1的弧,从列向汇点t连一条容量为1的弧,再对于每个重要位置(x,y),连一条从x指向y容量为1的弧,跑一次s-t的最大流,最大流出现是当源点s连出去的弧或者连向汇点的弧全部满载(表示图中行或者列全部被选完)的时候,即完成了二分图最大匹配==最小点集覆盖。有障碍物的情况下需要拆行拆列。

  1 #include <cstdio>
  2 #include <set>
  3 #include <queue>
  4 #include <cstring>
  5 #include <vector>
  6 #include <algorithm>
  7 using namespace std;
  8
  9 const int maxn = 10000 + 5;
 10 const int INF = 0x3f3f3f3f;
 11
 12 struct Edge {
 13     int from, to, cap, flow;
 14     Edge(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f) {}
 15 };
 16
 17 struct EdmondsKarp {
 18     int n, m;
 19     vector<Edge> edges;
 20     vector<int> G[maxn];
 21     int a[maxn];
 22     int p[maxn];
 23
 24     void init(int n) {
 25         for (int i = 0; i < n; i++) G[i].clear();
 26         edges.clear();
 27     }
 28
 29     void AddEdge(int from, int to, int cap) {
 30         edges.push_back(Edge(from, to, cap, 0));
 31         edges.push_back(Edge(to, from, 0, 0));
 32         m = edges.size();
 33         G[from].push_back(m - 2);
 34         G[to].push_back(m - 1);
 35     }
 36
 37     int Maxflow(int s, int t) {
 38         int flow = 0;
 39         for (;;) {
 40             memset(a, 0, sizeof(a));
 41             queue<int> Q;
 42             Q.push(s);
 43             a[s] = INF;
 44             while (!Q.empty()) {
 45                 int x = Q.front(); Q.pop();
 46                 for (int i = 0; i < G[x].size(); i++) {
 47                     Edge& e = edges[G[x][i]];
 48                     if (!a[e.to] && e.cap > e.flow) {
 49                         p[e.to] = G[x][i];
 50                         a[e.to] = min(a[x], e.cap - e.flow);
 51                         Q.push(e.to);
 52                     }
 53                 }
 54                 if (a[t]) break;
 55             }
 56             if (!a[t]) break;
 57             for (int u = t; u != s; u = edges[p[u]].from) {
 58                 edges[p[u]].flow += a[t];
 59                 edges[p[u] ^ 1].flow -= a[t];
 60             }
 61             flow += a[t];
 62         }
 63         return flow;
 64     }
 65 };
 66
 67 EdmondsKarp g;
 68
 69 int mp[105][105];
 70 int row[105][105];
 71 int col[105][105];
 72
 73 int main() {
 74     int T;
 75     scanf("%d", &T);
 76     while (T--) {
 77         int r, c, p;
 78         scanf("%d%d%d", &r, &c, &p);
 79         memset(mp, 0, sizeof(mp));
 80         int x, y;
 81         while (p--) {
 82             scanf("%d%d", &x, &y);
 83             mp[x][y] = 1;
 84         }
 85         scanf("%d", &p);
 86         while (p--) {
 87             scanf("%d%d", &x, &y);
 88             mp[x][y] = 2;
 89         }
 90         memset(row, 0, sizeof(row));
 91         memset(col, 0, sizeof(col));
 92         int cnt = 0;
 93         for (int i = 1; i <= r; i++) {
 94             bool ob = true;
 95             for (int j = 1; j <= c; j++) {
 96                 if (mp[i][j] == 1) {
 97                     if (ob) cnt++;
 98                     row[i][j] = cnt;
 99                     ob = false;
100                 } else if (mp[i][j] == 2) ob = true;
101             }
102         }
103         for (int j = 1; j <= c; j++) {
104             bool ob = true;
105             for (int i = 1; i <= r; i++) {
106                 if (mp[i][j] == 1) {
107                     if (ob) cnt++;
108                     col[i][j] = cnt;
109                     ob = false;
110                 } else if (mp[i][j] == 2) ob = true;
111             }
112         }
113         g.init(cnt + 2);
114         set<int> st;
115         for (int i = 1; i <= r; i++)
116             for (int j = 1; j <= c; j++)
117                 if (row[i][j]) st.insert(row[i][j]);
118         for (set<int>::iterator it = st.begin(); it != st.end();  it++)
119             g.AddEdge(0, *it, 1);
120         st.clear();
121         for (int j = 1; j <= c; j++)
122             for (int i = 1; i <= r; i++)
123                 if (col[i][j]) st.insert(col[i][j]);
124         for (set<int>::iterator it = st.begin(); it != st.end();  it++)
125             g.AddEdge(*it, cnt + 1, 1);
126         for (int i = 1; i <= r; i++)
127             for (int j = 1; j <= c; j++)
128                 if (mp[i][j] == 1) g.AddEdge(row[i][j], col[i][j], 1);
129         printf("%d\n", g.Maxflow(0, cnt + 1));
130     }
131     return 0;
132 }
时间: 2024-10-21 13:26:05

UVa12549 Sentry Robots (二分图最大匹配,最小点集覆盖)的相关文章

POJ 1325 Machine Schedule (二分图最小点集覆盖 匈牙利算法)

Machine Schedule Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 12621   Accepted: 5399 Description As we all know, machine scheduling is a very classical problem in computer science and has been studied for a very long history. Scheduli

POJ 1422 Air Raid (二分图最小点集覆盖 匈牙利算法)

Air Raid Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7236   Accepted: 4295 Description Consider a town where all the streets are one-way and each street leads from one intersection to another. It is also known that starting from an i

HDU2119Matrix(最小点集覆盖)

题意: n*m的矩阵由0,1组成,现在每次操作可以选择一行或者一列,把这一行或者一列的1都变成0,问最少操作几次,可以把1去完. 思路:把矩阵按X,Y坐标分为二分图的A,B俩集合,1的点,可以由其的横坐标向纵坐标连边,最后就是求建立的图的最小点集覆盖(因为你选择一个横坐标的话,肯定是想能最多的把这个横坐标对应的所有的纵坐标都选择到,画个图就很清晰了),最小点集覆盖等于最大匹配 #include<cstdio> #include<iostream> #include<algor

POJ 3020:Antenna Placement(无向二分图的最小路径覆盖)

Antenna Placement Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6334   Accepted: 3125 Description The Global Aerial Research Centre has been allotted the task of building the fifth generation of mobile phone nets in Sweden. The most st

POJ 1422 Air Raid(二分图匹配最小路径覆盖)

POJ 1422 Air Raid 题目链接 题意:给定一个有向图,在这个图上的某些点上放伞兵,可以使伞兵可以走到图上所有的点.且每个点只被一个伞兵走一次.问至少放多少伞兵 思路:二分图的最小路径覆盖,每个点拆成两个点,然后根据有向边连边,然后答案为n - 最大匹配数 代码: #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace

POJ 3020 Antenna Placement ,二分图的最小路径覆盖

题目大意: 一个矩形中,有N个城市'*',现在这n个城市都要覆盖无线,若放置一个基站,那么它至多可以覆盖相邻的两个城市. 问至少放置多少个基站才能使得所有的城市都覆盖无线? 无向二分图的最小路径覆盖 = 顶点数 –  最大二分匹配数/2 路径覆盖就是在图中找一些路径,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联: #include<cstdio> #include<cstring> #include<vector> #include<algor

ZOJ 1654 Place the Robots 二分图最大匹配

唉,又是神一样的建模,表示完全想不到. 题意是给你一块地,上面有空地,草地,障碍三种地形,然后让你在上面放机器人,机器人只能放在空地上.机器人会向上下左右四个方向发出攻击,机器人的攻击可以穿过草地但是无法穿过障碍.问你在不会是机器人相互攻击的前提下,最多能放多少个机器人. 我觉得大致的思路应该是这样的,首先会想当然的想到把能够相互攻击到的空地连边,然后求最大独立集,但最大独立集不好求,所以要想办法把它转化成最大匹配问题. 所以要想办法把空地变成边,首先一块空地肯定是有他的横坐标和纵坐标,可以表示

poj 3041 二分图最小点集覆盖

定理:二分图的最大匹配=最小点覆盖. 思路:将所有行看做点集X,所有列看做点集Y,如果在[i, j]处有小行星,则建立一条从i到j的边,然后求最大匹配即为最小点覆盖数即为答案. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int N = 501; 7 const int M = N * N; 8 int head[N]; 9

poj 3041 Asteroids (二分图最大匹配 == 最小点覆盖数)

应该属于最基本的匹配问题,重点在于为什么可以把行和列化为二分图的左右两个集合,理解好长时间,可以尝试这样理解:一个炸弹只能炸掉一行 或着 一列,左右两个集合中的值分别代表某一行或着某一列,因为连线的意义是如果某一行某一列锁定的值有行星才连线,我们所要求的是最少的炸弹数即最少的行数和列数之和即选出最少的行数和列数从左右两个集合中,这些行和列满足的要求是能够覆盖所有的边,换句话说这些行和列的炸弹能够炸掉所有的行星.所以实质上就成了求最小的点集覆盖 == 最大匹配数. /*==============