题意:给你一个二分图,选出最少的点使得覆盖的边数最多。查阅过之后发现这就是常说的二分图中最小点覆盖问题,而最小点覆盖问题是可以转化为二分图最大匹配问题的,
详见 二分图最大匹配的König定理及其证明
二分图最大匹配:对于二分图G的一个子图M,若M为其边数最多的子图,则称M为G的最大匹配。
那么问题来了,二分图最大匹配如何求呢,自然匈牙利算法是常用解法,但是复杂度
O
(
n
3
)
O(n^3)
O(n3)。这题基本上就可以舍弃了。
我们可以通过优秀的建图把问题转化为一个最大流问题,我们把每一条边的流量设置为1(反向边为0),建造源点和汇点(我是源点start=0,汇点goal=n+m+1),那么流到了汇点,最大流量就是网络流。
dinic,过:
#include<bits/stdc++.h>
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define maxn 3000005
#define maxm 55
#define hrdg 1000000007
#define zh 16711680
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
using namespace std;
inline int read(){
char c=getchar();long long x=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
int a, b, m, u, v, d, max_flow, depth[maxn];
int start, goal;
struct Edge{ int nex, to, dis;}edge[maxn*5];
int head[maxn*5], tot, cur[maxn*5]; //除了变量设计,dinic就是从H题复制粘贴过来的没必要看了
void add_egde(int u, int v, int d){
edge[tot] = {head[u], v, d}; head[u] = tot++;
edge[tot] = {head[v], u, 0}; head[v] = tot++;
}
bool bfs(int start, int goal){
memset (depth, -1, sizeof(depth));
queue <int> q;
depth[start] = 0;
q.push(start);
while (!q.empty())
{
int now = q.front(); q.pop();
for (int i = head[now]; ~i; i = edge[i].nex)
{
int to = edge[i].to;
if (depth[to] < 0 && edge[i].dis > 0)
{
depth[to] = depth[now] + 1;
q.push(to);
}
}
}
return depth[goal] > 0;
}
int dfs(int now, int goal, int limit) {
if(now == goal) return limit;
for(int & i = cur[now]; ~i; i = edge[i].nex) {
int to = edge[i].to;
if(edge[i].dis > 0 && depth[to] > depth[now]) {
int d = dfs(to, goal, min(limit, edge[i].dis));
if(d > 0) {
edge[i].dis -= d;
edge[i ^ 1].dis += d;
return d;
}
}
}
depth[now] = -1;
return 0;
}
void init(){
memset(head, -1, sizeof(head));
tot = 0;
}
int dinic(int start, int goal){
int flow = 0, f;
while (bfs(start, goal))
{
for (int i = start; i <= goal; i++)
cur[i] = head[i];
while (f = dfs(start, goal, inf))
flow += f;
}
return flow;
}
int main()
{
init();
a = read(); b = read(); m = read();
start = 0; goal = a + b + 1; //源点和汇点
for (int i = 1; i <= m; i++)
{
u = read(); v = read();
add_egde(u, v + a, 1); //正向边的权值设为1
}
for (int i = 1; i <= a; i ++)
add_egde(start, i, 1); //源点流出
for (int i = a + 1; i <= goal - 1; i++)
add_egde(i, goal, 1); //汇点集合
printf("%d\n", dinic(start, goal));
return 0;
}