最小割之最大密度子图
n n n 个点 m m m 条边的无向图 G = { V , E } G=\left\{V,E\right\} G={V,E},求最大密度子图 G ′ = { V ′ , E ′ } G'=\left\{V',E'\right\} G′={V′,E′} ,即子图有 M a x i m i z e ∣ E ′ ∣ ∣ V ′ ∣ Maximize \frac{|E'|}{|V'|} Maximize∣V′∣∣E′∣ 。
分析
答案 M a x i m i z e ∣ E ′ ∣ ∣ V ′ ∣ = ∑ e ∈ E x e ∑ v ∈ V x v \Huge{答案 Maximize \frac{|E'|}{|V'|} =\frac{\sum_{e\in E}x_e}{\sum_{v\in V}x_v}} 答案Maximize∣V′∣∣E′∣=∑v∈Vxv∑e∈Exe
其中 x e , x v ∈ 0 , 1 x_e, x_v \in {0,1} xe,xv∈0,1,如果是 1 1 1 表示在子图中, 0 0 0 表示不在子图中
并且如果边 ( u , v ) (u,v) (u,v) 在子图中,边 ( u , v ) (u,v) (u,v) 的两个端点 u , v u,v u,v 也必然在子图中。
设密度为
g
g
g
即为
g
=
∑
e
∈
E
x
e
∑
v
∈
V
x
v
g =\frac{\sum_{e\in E}x_e}{\sum_{v\in V}x_v}
g=∑v∈Vxv∑e∈Exe
变形:
∑
e
∈
E
x
e
=
∑
v
∈
V
x
v
g
⇔
∑
e
∈
E
x
e
−
∑
v
∈
V
x
v
g
=
0
\sum_{e\in E}x_e = {\sum_{v\in V}x_v} g \\ \Leftrightarrow \sum_{e\in E}x_e - {\sum_{v\in V}x_v} g = 0
e∈E∑xe=v∈V∑xvg⇔e∈E∑xe−v∈V∑xvg=0
我们构造 h ( g ) = ∑ e ∈ E ′ x e − ∑ v ∈ V ′ g ⋅ x v h(g) = \sum_{e \in E'}x_e - \sum_{v \in V'}g \cdot x_v h(g)=∑e∈E′xe−∑v∈V′g⋅xv
若最优密度为 D D D , 我们可以得到:
{ h ( g ) < 0 g > D h ( g ) = 0 g = D h ( g ) > 0 g < D \begin{cases}h(g)<0 & g>D \\ h(g)=0 & g=D\\ h(g)>0 & g<D\end{cases} ⎩ ⎨ ⎧h(g)<0h(g)=0h(g)>0g>Dg=Dg<D
所以,问题就转化为了求一个 g g g ,使得 h ( g ) = 0 h(g) = 0 h(g)=0
很明显我们可以二分答案 g g g , 可以发现对于每条边 e ( u , v ) ∈ E e(u,v)\in E e(u,v)∈E,要加入子图中的前提是 ( u , v ) (u,v) (u,v) 都在图中,所以容易发现这可以用最大权闭合图来解决
- 每条边作为一个点,向这条边的两个端点连 I N F INF INF 的边
- 源点向所有原图中的边连边,容量为 1 1 1
- 原图中的点向汇点连边,容量为 g g g
代码
#include <bits/stdc++.h>
using namespace std;
#define N 110
#define M 1500
#define INF 1e10
const double eps = 1e-8;
struct edge{
int u, v;
}edges[M];
int n, m, S, T;
int deg[N];
struct node{
int to, nxt;
double w;
}edge[M];
int head[N], tot;
void add(int u, int v, double c1, double c2){
edge[tot].to=v, edge[tot].w=c1, edge[tot].nxt=head[u], head[u]=tot++;
edge[tot].to=u, edge[tot].w=c2, edge[tot].nxt=head[v], head[v]=tot++;
}
void build(double g){
memset(head, -1, sizeof head), tot=0;
for(int i=1; i<=m; i++) add(edges[i].u, edges[i].v, 1, 1);
for(int i=1; i<=n; i++) add(S, i, m, 0), add(i, T, m+2*g-deg[i], 0);
}
int dep[N], cur[N];
bool bfs(){
memset(dep, -1, sizeof dep);
queue<int> Q;
Q.push(S);
dep[S] = 0;
cur[S] = head[S];
while(Q.size()){
int u = Q.front();
Q.pop();
for(int i = head[u]; ~i; i = edge[i].nxt){
int v = edge[i].to;
if(dep[v] == -1 && edge[i].w > eps){
dep[v] = dep[u]+1;
cur[v] = head[v];
if(v == T) return true;
Q.push(v);
}
}
}
return false;
}
double find(int u, double limit){
if(u == T){
return limit;
}
double flow = 0;
for(int i = cur[u]; ~i && flow < limit; i = edge[i].nxt){
int v = edge[i].to;
cur[u] = i;
if(dep[v] == dep[u] + 1 && edge[i].w > eps){
double t = find(v, min(limit - flow, edge[i].w));
if(t < eps) dep[v] = -1;
edge[i].w -= t;
edge[i ^ 1].w += t;
flow += t;
}
}
return flow;
}
double dinic(double g){
build(g);
double res = 0, flow;
while(bfs()){
while(flow = find(S, INF)){
res += flow;
}
}
return res;
}
int res = 0;
bool vis[N];
void dfs(int u){
vis[u] = true;
if(u != S) res ++ ;
for(int i = head[u]; ~i; i = edge[i].nxt){
int v = edge[i].to;
if(edge[i].w > 0 && !vis[v]) dfs(v);
}
}
int main(){
scanf("%lld %lld", &n, &m);
S = 0;
T = n + 1;
for(int i = 1;i <= m;i ++ ){
int u, v;
scanf("%lld %lld", &u, &v);
edges[i] = {u, v};
deg[u] ++ ;
deg[v] ++ ;
}
double l = 0, r = m;
while(l + eps < r){
double mid = (l + r) / 2;
if(m * n - dinic(mid) > eps) l = mid;
else r = mid;
}
dinic(l);
dfs(S);
if(!res){
puts("1\n1");
return 0;
}
cout<<res<<endl;
for(int i = 1; i <= n; i++)
if(vis[i]) printf("%d\n",i);
return 0;
}