http://poj.org/problem?id=3155
大致题意:给出一个无向图,求出它的一个最大密度子图,最大密度子图定义为子图的边数与顶点数的比值。
本题要注意的地方:
当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;
}