poj 3155 Hard Life 【最大密度子图】 【0-1分数规划 + 最小割】

Hard Life
Time Limit: 8000MS   Memory Limit: 65536K
Total Submissions: 7752   Accepted: 2230
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.

poj 3155 Hard Life 【最大密度子图】 【0-1分数规划 + 最小割】_第1张图片

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 54. If we add person number 3 to the team then hardness factor decreases to 65.

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 ≤ aibi ≤ nai ≠ 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.


题意:给一个N个点和M条边的无向图,让你输出它的任一个最大密度子图。


详解见神级论文   点我


对于mid值建图:设置超级源点source,超级汇点sink

1,source向所有点建边,容量为M;

2,所有点向sink建边,容量为M + 2*mid - d,其中d为该点在无向图中的度数;

3,对于无向边<u, v>,建边u->v 和 v->u,容量为1。


实现:

1,二分枚举比值mid,根据mid值建图,若(M * N – Maxflow())/ 2大于或等于0, l = mid否则r = mid;

2,最后得出的l就是最大密度,根据l值重新建图,求一次最大流Maxflow();

3,在残量网络里面,找到从源点出发能够到达的所有点,这些点就是我们要找的子图里面的点。




注意对于本题有个定理:无向图G中,任意两个具有不同密度的子图G1、G2,密度差不小于1/N/N。

这个定理很重要,它决定了我们二分查找的精度。 while(r - l >= 1/ N / N),这里查找的精度会决定你的代码是AC还是WA。

这里借用大牛的说法

对于这种输入数据:

3 3

1 2

2 3

3 1

最后一次网络流的运行会使所有从s出发的边满流,这时候子图的顶点数为0,结果
输出为0,不符合题意。但是由于误差最多不会超过1/(n*n),所以取l再做一次网络
流然后查解输出就可以了(解就是所有从s通过最后的参量网络可达的顶点——因为
这保证了不走割边,这就和论文中的讲解一致了)。




AC代码:

#include <cstdio>
#include <cstring>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define MAXN 110
#define MAXM 10000
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
struct Edge
{
    int from, to;
    double cap, flow;
    int next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], cur[MAXN];
bool vis[MAXN];
int N, M;
int x[1010], y[1010];
int d[MAXN];
void input()
{
    for(int i = 1; i <= N; i++)
        d[i] = 0;
    for(int i = 1; i <= M; i++)
    {
        scanf("%d%d", &x[i], &y[i]);
        d[x[i]]++; d[y[i]]++;
    }
}
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, double w)
{
    Edge E1 = {u, v, w, 0, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
int source, sink;
void getMap(double mid)
{
    for(int i = 1; i <= M; i++)
    {
        addEdge(x[i], y[i], 1.0);
        addEdge(y[i], x[i], 1.0);
    }
    for(int i = 1; i <= N; i++)
    {
        addEdge(source, i, M*1.0);
        addEdge(i, sink, M*1.0 + 2*mid - d[i]*1.0);
    }
}
bool BFS(int s, int t)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u  = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap - E.flow > eps)
            {
                dist[E.to] = dist[u] + 1;
                if(E.to == t) return true;
                vis[E.to] = true;
                Q.push(E.to);
            }
        }
    }
    return false;
}
double DFS(int x, double a, int t)
{
    if(x == t || fabs(a) < eps) return a;
    double flow = 0, f;
    for(int &i = cur[x]; i != -1; i = edge[i].next)
    {
        Edge &E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap-E.flow), t)) > eps)
        {
            edge[i].flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(fabs(a) < eps) break;
        }
    }
    return flow;
}
double Maxflow(int s, int t)
{
    double flow = 0;
    while(BFS(s, t))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(s, INF, t);
    }
    return flow;
}
void find_S(int u)
{
    vis[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        Edge E = edge[i];
        if(!vis[E.to] && E.cap - E.flow > eps)
            find_S(E.to);
    }
}
void solve()
{
    double l = 0, r = M, mid, t;
    source = 0, sink = N+1;
    while(r - l >= 1.0 / N / N)//精度已经限制
    {
        mid = (l + r) / 2;
        init();
        getMap(mid);
        double h = (N*M*1.0 - Maxflow(source, sink)) / 2;
        if(h >= eps)
            l = mid;
        else
            r = mid;
    }
    init();
    getMap(l);
    Maxflow(source, sink);
    memset(vis, false, sizeof(vis));
    find_S(source);
    int ans = 0;
    for(int i = 1; i <= N; i++)
        if(vis[i]) ans++;
    printf("%d\n", ans);
    for(int i = 1; i <= N; i++)
        if(vis[i])
            printf("%d\n", i);
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        if(M == 0)//注意M为0时的输出
        {
            printf("1\n1\n");
            continue;
        }
        input();
        solve();
    }
    return 0;
}



你可能感兴趣的:(poj 3155 Hard Life 【最大密度子图】 【0-1分数规划 + 最小割】)