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

链接POJ - 3155 Hard Life

题意:

给定一个含有 n n 个结点, m m 条边的无向图 G G ,找出其一个子图 G ( V , E ) G'(V,E) ,其边的条数 E |E| 和点的个数 V |V| 的比值,即 E V \frac{|E|}{|V|} 最大。(子图 G G' ( u , v ) E \forall (u,v)\in E ,有 u V v V u\in V\land v\in V

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



分析:

即求:
M a x i m i z e        D = E V Maximize\;\;\;D=\frac{|E|}{|V|}
对于一个答案猜测值 g g ,构造函数:
h ( g ) = max { E g V } h(g)=\max\{\,|E|-g\cdot|V|\,\}
设正确答案为 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 ) 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|\,\}

所以,可以将 e e 均视为点权为1的点 v v 的点权均为 g -g ,构成新图,于是求解 h ( g ) h(g) 即 求解新图的 最大权闭合图

若原图存在边 ( u , v ) (u,v) ,则新图中建边 < [ u v ] , u > <[uv],u> < [ u v ] , 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;
}
发布了214 篇原创文章 · 获赞 40 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Ratina/article/details/102087943
今日推荐