POJ 3155 最大密度子图

来源:互联网 发布:社交网络红包广告 编辑:程序博客网 时间:2024/05/23 21:33
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;    }

0 0
原创粉丝点击