pku 3155 Hard Life 最大密集子图

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

题意:

给定一个无向图,求该图的一个子图使得该子图中边/点的权值最大;

思路:

证明看论文:http://wenku.baidu.com/view/986baf00b52acfc789ebc9a9.html

建图方法:

把原图中的无向边转换成两条有向边,容量为1;

设一源点,连接所有点,容量为U

设一汇点,所有点连接汇点,容量为 U+2g-du

二分枚举最大密度g,其中du为u的度。

U一般取边数即可。

//#pragma comment(linker,"/STACK:327680000,327680000")

#include <iostream>

#include <cstdio>

#include <cmath>

#include <vector>

#include <cstring>

#include <algorithm>

#include <string>

#include <set>

#include <functional>

#include <numeric>

#include <sstream>

#include <stack>

#include <map>

#include <queue>



#define CL(arr, val)    memset(arr, val, sizeof(arr))



#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define ll long long

#define L(x)    (x) << 1

#define R(x)    (x) << 1 | 1

#define MID(l, r)   (l + r) >> 1

#define Min(x, y)   (x) < (y) ? (x) : (y)

#define Max(x, y)   (x) < (y) ? (y) : (x)

#define E(x)        (1 << (x))

#define iabs(x)     (x) < 0 ? -(x) : (x)

#define OUT(x)  printf("%I64d\n", x)

#define lowbit(x)   (x)&(-x)

#define Read()  freopen("din.txt", "r", stdin)

#define Write() freopen("dout.txt", "w", stdout);





#define M 1000007

#define N 1107



using namespace std;



const double inf = ~0u;

const double eps = 1e-6;

double lim;



struct node

{

    int v;

    double w;

    int next;

}g[M];

int head[N],ct;

int q[N*100];



int level[N];

bool vt[N];



struct t_node

{

    int u,v;

}t_g[N];



int path[N],len;

int du[N];

int n,m;



void add(int u,int v,double w)

{

    g[ct].v = v;

    g[ct].w = w;

    g[ct].next = head[u];

    head[u] = ct++;



    g[ct].v = u;

    g[ct].w = 0;

    g[ct].next = head[v];

    head[v] = ct++;

}

bool layer(int s,int e){

    int i;

    CL(level,-1);

    level[s] = 0;

    int l,r;

    l = r = 0;

    q[r] = s;

    while (l <= r){

        int u = q[l++];

        for (i = head[u]; i != -1; i = g[i].next){

            int v = g[i].v;

            if (level[v] == -1 && g[i].w > eps){

                level[v] = level[u] + 1;

                q[++r] = v;

                if (v == e) return true;

            }

        }

    }

    return false;

}

double find(int s,int e){

    int i;

    double ans = 0;

    int top = 1;



    while (top){

        int u = (top == 1 ? s : g[q[top - 1]].v);//如果没有变肯定是起点,否则就是上一个边终点



        if (u == e){

            double MIN = inf;

            int pos;

            //找出最小流量

            for (i = 1; i < top; ++i){

                int tp = q[i];//注意这里取边的编号

                if (g[tp].w < MIN){

                    MIN = g[tp].w;

                    pos = i;

                }

            }

            //更新容量

            for (i = 1; i < top; ++i){

                int tp = q[i];//注意这里取边的编号

                g[tp].w -= MIN;

                g[tp^1].w += MIN;

            }

            ans += MIN;

            top = pos;

        }

        else{//找可行流

            for (i = head[u]; i != -1; i = g[i].next){

                int v = g[i].v;

                if (g[i].w > eps && level[v] == level[u] + 1){

                    q[top++] = i;

                    break;

                 }

            }

            if (i == -1){//如果u没有可走的子节点

                top--;

                level[u] = -1;

            }

        }

    }

    return ans;

}

double dinic(int s,int e,double mid)

{

    int i;

    //建图是关键

    CL(head,-1); ct = 0;

    for (i =  1; i <= m; ++i)

    {

        int u = t_g[i].u;

        int v = t_g[i].v;

        add(u,v,1.0); add(v,u,1.0);

    }

    for (i = 1; i <= n; ++i)

    {

        add(s,i,m);

        add(i,e,1.0*m + 2*mid - du[i]);

    }

    //求最小割

    double ans = 0;

    while (layer(s,e)) ans += find(s,e);

    return ans;

}

void dfs(int s)

{

    vt[s] = true;

    for (int i = head[s]; i != -1; i = g[i].next)

    {

        int v = g[i].v;

        double w = g[i].w;

        if (!vt[v] &&  w > eps)

        {

            path[len++] = v;

            dfs(v);

        }

    }

}

int main()

{

    //Read();

    int i;

    while (~scanf("%d%d",&n,&m))

    {

        if (m == 0)

        {

            printf("1\n1\n");

            continue;

        }

        CL(du,0);

        for (i = 1; i <= m; ++i)

        {

            scanf("%d%d",&t_g[i].u,&t_g[i].v);

            du[t_g[i].u]++;

            du[t_g[i].v]++;

        }

        double l = 1.0/n;

        double r = m*1.0;

        double mid = 0;

        int s = 0;

        int e = n + 1;

        lim = 1.0/(n*n);

        while (r - l > lim)//证明有:任意两个最大密度子图的差值不会超过1/(n*n)

        {

            mid = (l + r)/2;

            double tmp = dinic(s,e,mid);

            double ans = (m*n - tmp)/2.0;//得到值

            if (ans > eps) l = mid;

            else r = mid;

        }

        dinic(s,e,l);

        CL(vt,false);

        len = 0;

        dfs(s);

        sort(path,path + len);

        printf("%d\n",len);

        for (i = 0; i < len; ++i) printf("%d\n",path[i]);

    }

    return 0;

}

  

 

你可能感兴趣的:(life)