该题和HDU 5093 如出一辙 传送门 。即求解二分图最大匹配数 = 最小点集覆盖 。 该题要求用尽量少的机器人看守所有重要的点,并且障碍物会阻隔机器人的看守范围 。
我们不妨将行列分开,按照行和列的最大看守范围编号,这样得到的就是最大匹配数 。 由于要求看守所有重要的点,所以这样可以有效去重,进行了最大匹配之后还可以保证一定看守了所有的点 。 建好图之后套Dinic模板就行了 。
该题有点逆着来的意思 ,请读者仔细品味 。
细节参见代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int INF = 1000000000; const int maxn = 20000 + 100; int T,cnt,n,m,u,pp,a,b,v,w,t,id1[100][100],id2[100][100]; char s[maxn][maxn]; struct Edge { int from, to, cap, flow; }; bool operator < (const Edge& a, const Edge& b) { return a.from < b.from || (a.from == b.from && a.to < b.to); } struct Dinic { int n, m, s, t; vector<Edge> old; vector<Edge> edges; // 边数的两倍 vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[maxn]; // BFS使用 int d[maxn]; // 从起点到i的距离 int cur[maxn]; // 当前弧指针 void init(int n) { for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from, int to, int cap) { edges.push_back((Edge){from, to, cap, 0}); edges.push_back((Edge){to, from, 0, 0}); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool BFS() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(s); vis[s] = 1; d[s] = 0; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) { vis[e.to] = 1; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) { if(x == t || a == 0) return a; int flow = 0, f; for(int& i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap-e.flow))) > 0) { e.flow += f; edges[G[x][i]^1].flow -= f; flow += f; a -= f; if(a == 0) break; } } return flow; } int Maxflow(int s, int t) { this->s = s; this->t = t; int flow = 0; while(BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, INF); } return flow; } }g; char ss[maxn][maxn]; map<int,int> p; int main() { scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&pp); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) s[i][j] = '.'; g.init(n*m*2+n); for(int i=1;i<=pp;i++) { scanf("%d%d",&a,&b); s[a][b] = '*'; } scanf("%d",&w); for(int i=1;i<=w;i++) { scanf("%d%d",&a,&b); s[a][b] = '#'; } int cnt = 1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(s[i][j] == '#') cnt++;//按照行编号 if(s[i][j] == '*') id1[i][j] = cnt; } cnt++; } for(int j=1;j<=m;j++) { for(int i=1;i<=n;i++) { if(s[i][j] == '#') cnt++;//按列编号 if(s[i][j] == '*') id2[i][j] = cnt; } cnt++; } p.clear(); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(s[i][j] == '*') { g.AddEdge(id1[i][j],id2[i][j],1);//连边 if(!p.count(id1[i][j])) { //每个编号只能与源点连一次 p[id1[i][j]] = 1; g.AddEdge(0,id1[i][j],1); } if(!p.count(id2[i][j])) {//同上 p[id2[i][j]] = 1; g.AddEdge(id2[i][j],cnt+1,1); } } } } int ans = g.Maxflow(0,cnt+1); printf("%d\n",ans); } return 0; }