POJ - 3155 Hard Life(最大密度子图)

链接POJ - 3155 Hard Life

题意:

给定一个含有 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|} VE最大。(子图 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 uVvV

要求按编号升序输出任意一个满足条件的子图中的点。



分析:

即求:
M a x i m i z e        D = ∣ E ∣ ∣ V ∣ Maximize\;\;\;D=\frac{|E|}{|V|} MaximizeD=VE
对于一个答案猜测值 g g g,构造函数:
h ( g ) = max ⁡ {   ∣ E ∣ − g ⋅ ∣ V ∣   } h(g)=\max\{\,|E|-g\cdot|V|\,\} h(g)=max{EgV}
设正确答案为 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)=0g=Dh(g)<0g>Dh(g)>0g<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{EgV}=max{1E+(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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值