poj3155 最大密度子图 分数规划
题意:给定一个无向图,选取一个密度最大的子图,就是边数/点数的比值最大,输出子图顶点,
设wi为子图边数,vi是子图点数,就是要max{wi-ans*vi}ans为二分的值,按边来考虑,如果选了一条边那么他连的两个端点都要被选,也就是
边也看做是点,点权为1,原来的点点权为-ans,求最大权闭合图就可以max这个值,然后类似最优比率生成树的二分下去(这是讲解)
/*
主算法:零一规划,用二分来猜测最大密度为g。。。构造函数h(g)=(|E'|-g*|V'|)
设D为最优解,
当h(g)<0,g>D;
当h(g)=0,g=D;
当h(g)>0,g<D;
网络流建图部分:
在原图点集V 的基础上增加源S和汇T;将每条原无向边(u,v)替换为两条容量为1 的有向边(u,v)和(v,u);
增加连接源S到原图每个点v的有向边(s,v) , 容量为U ;
增加连接原图每个点v 到汇T 的有向边(v,T),容量为(U+2*g-dv) ,其中dv为点v的度。
其中U为为了流量不出现负值而统一加的一个大数,取U=m即可。
到此有h(g)=(U*n-c[S,T])/2;[S,T]为最小割。
推算过程见胡伯涛论文~
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 308
#define edge 20800
#define inf 0x3f3f3f3f
#define eps 1e-5
int first[maxn],dis[maxn],num[maxn],du[maxn];
int vv[edge],nxt[edge];
double ww[edge];
bool vis[maxn];
int e,NN,n,m;
int sum = 0;
int ta[2000],tb[2000];
struct Edge
{
int u,v;
Edge(){}
Edge(int uu,int vv)
{
u = uu;v = vv;
}
};
void addedge(int u,int v,double w)//添加单向边
{
vv[e] = v; ww[e] = w; nxt[e] = first[u]; first[u] = e++;
vv[e] = u; ww[e] = 0; nxt[e] = first[v]; first[v] = e++;
}
void addEdge(int u,int v,double w)//添加双向边
{
vv[e] = v; ww[e] = w; nxt[e] = first[u]; first[u] = e++;
vv[e] = u; ww[e] = w; nxt[e] = first[v]; first[v] = e++;
}
inline double min(double a,double b)
{
return a>b?b:a;
}
double dfs(int u,int s,int d,double cost)
{
if(u == d) return cost;
double ans = 0;
int _min = NN;
for(int i = first[u];i != -1;i = nxt[i])
{
int v = vv[i];
if(ww[i] > eps)
{
if(dis[v] + 1 == dis[u])
{
double t = dfs(v,s,d,min(ww[i],cost));
ww[i] -= t;
ww[i^1] += t;
ans += t;
cost -= t;
if(dis[s] == NN) return ans;
if(cost <= eps) break;
}
if(_min > dis[v]) _min = dis[v];
}
}
if(ans <= eps)
{
if(--num[dis[u]] == 0) dis[s] = NN;
dis[u] = _min + 1;
++num[dis[u]];
}
return ans;
}
double isap(int s,int d)
{
memset(dis,0,sizeof(dis));
memset(num,0,sizeof(num));
num[0] = NN;
double ans = 0;
while(dis[s] < NN) ans += dfs(s,s,d,inf);
return ans;
}
void build(double g)
{
e = 0;NN = n + 2;
memset(first,-1,sizeof(first));
for(int i = 1;i <= n;i++)
{
addedge(0,i,m);
}
for(int i = 1;i <= m;i++)
{
addEdge(ta[i],tb[i],1);
}
for(int i = 1;i <= n;i++)
{
addedge(i,n+1,m+2*g-du[i]);
}
}
void dfs(int u)
{
vis[u] = 1;
for(int i = first[u];i != -1;i = nxt[i])
{
int v = vv[i];
if(ww[i] > 0 && !vis[v])
{
vis[v] = 1;
sum++;
dfs(v);
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&m)==2)
{
if(m == 0)
{
printf("%d\n%d\n",1,1);
continue;
}
memset(du,0,sizeof(du));
for(int i = 1;i <= m;i++)
{
scanf("%d%d",&ta[i],&tb[i]);
du[ta[i]]++;du[tb[i]]++;
}
double l = 0,r = m;
while(r - l >= 1./n/n)
{
double mid = (l + r)/2;
build(mid);
if((m*n - isap(0,n+1))/2 > eps)
{
l = mid;
}
else r = mid;
}
build(l);
memset(vis,0,sizeof(vis));
isap(0,n+1);
sum = 0;
dfs(0);
int ok = 0;
printf("%d\n",sum);
for(int i = 1;i <= n;i++)
{
if(vis[i])
{
printf("%d\n",i);
}
}
}
return 0;
}
POJ 3155 最大密度子图
最新推荐文章于 2024-01-02 21:07:04 发布
本文详细解析了POJ3155最大密度子图问题的解决方案,利用二分查找结合零一规划的方法确定最优子图,通过网络流算法实现具体计算流程。
2781

被折叠的 条评论
为什么被折叠?



