poj 3155 Hard Life(最大密度子图,01分数规划)

http://poj.org/problem?id=3155


大致题意:给出一个无向图,求出它的一个最大密度子图,最大密度子图定义为子图的边数与顶点数的比值。


详见amber论文中关于最大密度子图

本题要注意的地方:

当m为0时也要输出内容。

二分的边界是 1/n/n(high - low >1/n/n) ,这在amber论文引理4.1中有讲解

因为h函数的特性,恒有h >=0,即h函数不是一个严格递减的函数,当减小到0时便不再减小。那么我们要取得的是第一个为0的,所以要二分下界,而不是直接取mid,因为没有意识到函数特性,WA了N次。

二分完毕后,并不能求出正确的low值,ms也是因为该函数的特性,我们还要再求一遍最大流。

根据残量网络求最大密度子图:从源点dfs,走残量网络中流量大于0的边并标记。


#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#define LL long long
#define _LL __int64
#define eps 1e-8

using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 110;
const int maxm = 1010;

struct node
{
	int u,v;
	double w;
	int next,rev;
}p[maxm],edge[maxm*6];

int s,t,n,m,nn;
int cnt,head[maxn];
int d[maxn];
int dist[maxn],vis[maxn];
int ans;

void init()
{
	cnt = 0;
	memset(head,-1,sizeof(head));
}

void add(int u, int v, double w)
{
	edge[cnt] = (struct node){u,v,w,head[u],cnt+1};
	head[u] = cnt++;

	edge[cnt] = (struct node){v,u,0,head[v],cnt-1};
	head[v] = cnt++;
}

void build(double mid)
{
	init();
	for(int i = 1; i <= m; i++)
	{
		add(p[i].u,p[i].v,1.0);
		add(p[i].v,p[i].u,1.0);
	}
	for(int i = 1; i <= nn; i++)
	{
		add(s,i,m);
		add(i,t,m*1.0+2*mid-d[i]*1.0);
	}
}

bool bfs()
{
    queue <int> que;
    memset(dist, 0, sizeof(dist));
    memset(vis, 0, sizeof(vis));
    while(!que.empty()) que.pop();
    vis[s] = 1;
    que.push(s);
    while(!que.empty())
    {
        int u = que.front();
        que.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            if(edge[i].w && !vis[v])
            {
                que.push(v);
                vis[v] = 1;
                dist[v] = dist[u]+1;
            }
        }
    }
    if(dist[t] == 0)
		return false;
	return true;
}

double dfs(int u, double delta)
{
    if(u == t) return delta;
    double ret = 0,tmp;

	for(int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].v;
		if(edge[i].w && dist[edge[i].v] == dist[u]+1 && (tmp = dfs(v,min(delta,edge[i].w))))
		{
			edge[i].w -= tmp;
			edge[edge[i].rev].w += tmp;
			return tmp;
		}
	}
	if(!ret) dist[u] = -1;
	return ret;
}

double Dinic()
{
    double ans = 0,res;
    while(bfs())
    {
       while(res = dfs(s,INF))
			ans += res;
    }
    return ans;
}

void dfs_cut(int u)
{
	vis[u] = 1;
	if(u <= nn) ans++;
	for(int i = head[u]; i != -1; i = edge[i].next)
	{
		int v = edge[i].v;
		if(edge[i].w > 0 && !vis[v])
		{
			dfs_cut(v);
		}
	}
}

int main()
{
	while(~scanf("%d %d",&n,&m))
	{
		if(m == 0)
		{
			printf("1\n1\n");
			continue;
		}
		memset(d,0,sizeof(d));
		for(int i = 1; i <= m; i++)
		{
			scanf("%d %d",&p[i].u,&p[i].v);
			d[p[i].u]++;
			d[p[i].v]++;
		}

		s = n+1;
		t = n+2;
		nn = n;
		n = t;

		double low,high,mid,h;
		low = 0.0;
		high = m;
		while(high - low > 1.0/nn/nn)
		{
			mid = (high + low)/2;
			build(mid);
			h = (m*nn*1.0 - Dinic() )/2;

			if(h > eps)
				low = mid;
			else high = mid;
		}

		build(low);
		Dinic();

		ans = 0;
		memset(vis,0,sizeof(vis));

		dfs_cut(s);

		printf("%d\n",ans);
		for(int i = 1; i <= nn; i++)
		{
			if(vis[i])
				printf("%d\n",i);
		}
	}
	return 0;
}



你可能感兴趣的:(01分数规划,最大密度子图)