题意:
给定一个含有 n n n个结点, m m m条边的无向图 G G G,找出其一个子图 G ′ ( V , E ) G'(V,E) G′(V,E),其边的条数 ∣ E ∣ |E| ∣E∣和点的个数 ∣ V ∣ |V| ∣V∣的比值,即 ∣ E ∣ ∣ V ∣ \frac{|E|}{|V|} ∣V∣∣E∣最大。(子图 G ′ G' G′中 ∀ ( u , v ) ∈ E \forall (u,v)\in E ∀(u,v)∈E,有 u ∈ V ∧ v ∈ V u\in V\land v\in V u∈V∧v∈V )
要求按编号升序输出任意一个满足条件的子图中的点。
分析:
即求:
M
a
x
i
m
i
z
e
D
=
∣
E
∣
∣
V
∣
Maximize\;\;\;D=\frac{|E|}{|V|}
MaximizeD=∣V∣∣E∣
对于一个答案猜测值
g
g
g,构造函数:
h
(
g
)
=
max
{
∣
E
∣
−
g
⋅
∣
V
∣
}
h(g)=\max\{\,|E|-g\cdot|V|\,\}
h(g)=max{∣E∣−g⋅∣V∣}
设正确答案为
D
∗
D^{*}
D∗,则有:
{
h
(
g
)
=
0
⇔
g
=
D
∗
h
(
g
)
<
0
⇔
g
>
D
∗
h
(
g
)
>
0
⇔
g
<
D
∗
\begin{cases} h(g)=0\Leftrightarrow g=D^{*}\\ h(g)\lt0\Leftrightarrow g\gt D^{*}\\ h(g)\gt0\Leftrightarrow g\lt D^{*}\\ \end{cases}
⎩⎪⎨⎪⎧h(g)=0⇔g=D∗h(g)<0⇔g>D∗h(g)>0⇔g<D∗
所以函数
h
(
g
)
h(g)
h(g)具有单调性,可以二分查找答案。
对于函数 h ( g ) h(g) h(g),有 max { ∣ E ∣ − g ⋅ ∣ V ∣ } = max { 1 ⋅ ∣ E ∣ + ( − g ) ⋅ ∣ V ∣ } \max\{\,|E|-g\cdot|V|\,\}=\max\{\,1\cdot|E|+(-g)\cdot|V|\,\} max{∣E∣−g⋅∣V∣}=max{1⋅∣E∣+(−g)⋅∣V∣};
所以,可以将 边 e e e均视为点权为1的点,点 v v v的点权均为 − g -g −g,构成新图,于是求解 h ( g ) h(g) h(g) 即 求解新图的 最大权闭合图。
若原图存在边 ( u , v ) (u,v) (u,v),则新图中建边 < [ u v ] , u > <[uv],u> <[uv],u>与 < [ u v ] , v > <[uv],v> <[uv],v>,此外,由于容量需定为浮点型,所以比较时要注意精度。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<queue>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
const double eps=1e-18;
int s=0,t=maxn-1;
int head[maxn],cnt;
struct edge
{
int u,v;
double w;
int next;
}e[maxn];
void add_edge(int u,int v,double w)
{
e[cnt]=edge{u,v,w,head[u]};
head[u]=cnt++;
e[cnt]=edge{v,u,0,head[v]};
head[v]=cnt++;
}
int dis[maxn];
bool bfs()
{
memset(dis,-1,sizeof(dis));
queue<int> q;
q.push(s);
dis[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(e[i].w>eps&&dis[v]==-1)
{
dis[v]=dis[u]+1;
if(v==t)
return true;
q.push(v);
}
}
}
return false;
}
int cur[maxn];
double dfs(int u,double flow)
{
if(u==t)
return flow;
for(int &i=cur[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
if(dis[v]==dis[u]+1&&e[i].w>eps)
{
double k=dfs(v,min(flow,e[i].w));
if(k>eps)
{
e[i].w-=k;
e[i^1].w+=k;
return k;
}
}
}
return 0;
}
double dinic()
{
double ans=0;
while(bfs())
{
for(int i=0;i<maxn;i++)
cur[i]=head[i];
while(double k=dfs(s,INF))
ans+=k;
}
return ans;
}
int n,m;
pair<int,int> E[maxn];
bool check(double g)
{
memset(head,-1,sizeof(head));
cnt=0;
for(int i=1;i<=m;i++)
{
add_edge(s,n+i,1);
int u=E[i].first,v=E[i].second;
add_edge(n+i,u,INF);
add_edge(n+i,v,INF);
}
for(int i=1;i<=n;i++)
add_edge(i,t,g);
return m-dinic()>0;
}
vector<int> ans;
bool vis[maxn];
void DFS(int u)
{
vis[u]=true;
if(1<=u&&u<=n)
ans.push_back(u);
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].v;
double w=e[i].w;
if(!vis[v]&&w>eps)
DFS(v);
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d %d",&E[i].first,&E[i].second);
if(m==0)
{
printf("1\n1\n");
return 0;
}
double L=0,R=m,MID;
while(R-L>1e-5)
{
MID=(L+R)/2;
if(check(MID))
L=MID;
else
R=MID;
}
check(L);
DFS(s);
sort(ans.begin(),ans.end());
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++)
printf("%d\n",ans[i]);
return 0;
}