POJ 3155 Hard Life(最大密度子图)

来源:互联网 发布:au mac卡顿 编辑:程序博客网 时间:2024/05/17 22:33

Hard Life
Time Limit: 8000MS Memory Limit: 65536K
Total Submissions: 9111 Accepted: 2648
Case Time Limit: 2000MS Special Judge
Description

John is a Chief Executive Officer at a privately owned medium size company. The owner of the company has decided to make his son Scott a manager in the company. John fears that the owner will ultimately give CEO position to Scott if he does well on his new manager position, so he decided to make Scott’s life as hard as possible by carefully selecting the team he is going to manage in the company.

John knows which pairs of his people work poorly in the same team. John introduced a hardness factor of a team — it is a number of pairs of people from this team who work poorly in the same team divided by the total number of people in the team. The larger is the hardness factor, the harder is this team to manage. John wants to find a group of people in the company that are hardest to manage and make it Scott’s team. Please, help him.

In the example on the picture the hardest team consists of people 1, 2, 4, and 5. Among 4 of them 5 pairs work poorly in the same team, thus hardness factor is equal to 5⁄4. If we add person number 3 to the team then hardness factor decreases to 6⁄5.

Input

The first line of the input file contains two integer numbers n and m (1 ≤ n ≤ 100, 0 ≤ m ≤ 1000). Here n is a total number of people in the company (people are numbered from 1 to n), and m is the number of pairs of people who work poorly in the same team. Next m lines describe those pairs with two integer numbers ai and bi (1 ≤ ai, bi ≤ n, ai ≠ bi) on a line. The order of people in a pair is arbitrary and no pair is listed twice.

Output

Write to the output file an integer number k (1 ≤ k ≤ n) — the number of people in the hardest team, followed by k lines listing people from this team in ascending order. If there are multiple teams with the same hardness factor then write any one.

Sample Input

sample input #1
5 6
1 5
5 4
4 2
2 5
1 2
3 1

sample input #2
4 0
Sample Output

sample output #1
4
1
2
4
5

sample output #2
1
1
Hint

Note, that in the last example any team has hardness factor of zero, and any non-empty list of people is a valid answer.

Source

Northeastern Europe 2006

题目大意:

  有一张无向无权图,让你找出一个子图,使得子图中的边数/顶点数最大。

解题思路:

  最大密度子图的最简单情况,详细解法胡伯涛的《最小割模型在信息学中的应用》。这里只做简要说明。
  首先,最大密度子图是一个01分数规划问题,我们所使用的求解方法就是求解01分数规划问题的方法。
  记V=||E=||n=||m=||,那问题就变成了最大化D=mn。先确定二分的范围,答案一定属于[1V,E1](当原图不存在边时需要特判),接下来可以知道答案之间的距离一定不小于1V×V。对于当前的答案猜测值g,令h(g)=maxx{eE1×xevVg×xe}。为了求解当前的h(g),我们可以建图利用最小割求解。
  建图方式如下:首先定义一个足够大的常量U=E,以避免接下来边的容量出现负数。在原图的基础上增加源点s和汇点t。对于原图每一条边建立两条容量为1的边u,vv,u。从原点s向每个原图节点u建立一条有向边s,u,容量为U。从每个原图节点向汇点建立一条有向边u,t,容量为U2gdeg(u),其中deg(u)为原图中顶点u的度数,那么h(g)=U×Vc[s,t]2,其中c[s,t]为最小割。选择的节点即为从s出发进过没有满流的边可以到达的点。
  这里有一个细节,由于我们建成的图的最大流一定不会超过U×V,所以本来应该h(g)小于0的时候通过这个方式得到的h(g)是等于0的,而这时也无法找到选择的点,所以和大多数分数规划不一样这里要把h(g)小于0和等于0一起处理。也因为当h(g)等于0的时候无法找到选择的点,所以我们就没有办法使用Dinkelbach算法。
  这个写法还可以向边带权的无向图,以及点边都带权的无向图扩展。
  对于边带非负权的无向图:答案之间不再有最小差值,我们需要规定二分的精度。常量U需要该为所有边权值之和。重新定义deg(u)为与u相连的边的权值之和。对于每条从原图替换得到的边的容量改为原图中这条边的权值。其它地方不变。
  对于边带非负权点带可正可负的权值的无向图:同样需要规定二分精度。常量U该为点权绝对值和的两倍加上边权和。同样重新定义deg(u)。从源图替换得到的边的容量同样改为权值。每个节点到汇点的边容量改为U+2gdu2pu,其中pu为节点u的权值。其它不变。

AC代码:

#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <ctime>#include <vector>#include <queue>#include <stack>#include <deque>#include <string>#include <map>#include <set>#include <list>using namespace std;#define INF 0x3f3f3f3f#define LL long long#define fi first#define se second#define mem(a,b) memset((a),(b),sizeof(a))const double eps=1e-8;const int MAXV=100+3;const int MAXE=1000+3;struct Edge{    int to, rev;    double cap;    Edge(int t, double c, int r):to(t), cap(c), rev(r){}};pair<int, int> edge[MAXE];int N, V, E, U, cnt;int deg[MAXV];//原图中点的度数vector<Edge> G[MAXV];int level[MAXV];int iter[MAXV];bool vis[MAXV];void add_edge1(int from, int to, double cap)//建立有向边{    G[from].push_back(Edge(to, cap, G[to].size()));    G[to].push_back(Edge(from, 0.0, G[from].size()-1));}void add_edge2(int from, int to, double cap)//建立双向边{    G[from].push_back(Edge(to, cap, G[to].size()));    G[to].push_back(Edge(from, cap, G[from].size()-1));}void bfs(int s){    mem(level, -1);    queue<int> que;    level[s]=0;    que.push(s);    while(!que.empty())    {        int u=que.front(); que.pop();        for(int i=0;i<G[u].size();++i)        {            Edge &e=G[u][i];            if(e.cap>eps && level[e.to]<0)            {                level[e.to]=level[u]+1;                que.push(e.to);            }        }    }}double dfs(int u, int t, double f){    if(u==t)        return f;    for(int &i=iter[u];i<G[u].size();++i)    {        Edge &e=G[u][i];        if(e.cap>0 && level[u]<level[e.to])        {            double d=dfs(e.to, t, min(f, e.cap));            if(d>eps)            {                e.cap-=d;                G[e.to][e.rev].cap+=d;                return d;            }        }    }    return 0;}double dinic(int s, int t)//最大流dinic算法{    double flow=0;    while(true)    {        bfs(s);        if(level[t]<0)            return flow;        mem(iter, 0);        double f;        while((f=dfs(s, t, INF))>eps)            flow+=f;    }}void init()//初始化{    V=N+2;    U=E;    for(int i=0;i<V;++i)    {        deg[i]=0;        vis[i]=false;    }    cnt=0;}double judge(double x){    // 0     源点    // 1 ~ N 原图节点    // N+1   汇点    int s=0, t=N+1;    for(int i=0;i<V;++i)        G[i].clear();    for(int i=0;i<E;++i)        add_edge2(edge[i].fi, edge[i].se, 1.0);    for(int i=1;i<=N;++i)    {        add_edge1(s, i, U);        add_edge1(i, t, U+2*x-deg[i]);    }    return (U*N-dinic(s, t))/2;}void dfs2(int u)//寻找可以通过没有满流的边到达的点{    vis[u]=true;    ++cnt;    for(int i=0;i<G[u].size();++i)    {        int v=G[u][i].to;        if(!vis[v] && G[u][i].cap>0)            dfs2(v);    }}int main(){    while(~scanf("%d%d", &N, &E))    {        if(E==0)        {            puts("1\n1");            continue;        }        init();        for(int i=0;i<E;++i)        {            scanf("%d%d", &edge[i].fi, &edge[i].se);            ++deg[edge[i].fi];            ++deg[edge[i].se];        }        double l=1.0/N, r=E/1.0, min_dif=1.0/N/N;        while(r-l>=min_dif)        {            double mid=(l+r)/2;            if(judge(mid)>eps)                l=mid;            else r=mid;        }        judge(l);//对最终结果重新建图以找到选择的点        dfs2(0);        printf("%d\n", cnt-1);        for(int i=1;i<=N;++i)            if(vis[i])                printf("%d\n", i);    }    return 0;}
原创粉丝点击