【01分数规划】最大密度子图

http://www.cnblogs.com/gufeiyang/archive/2012/10/04/2711618.html


题意:给出一个无向图, 求这个无向图的最大密度子图。  就是选择一个子图,使边的数目比上点的数目最大,并且输出方案。

 

思路: 这道题真心不会。 可以确定的是一道01规划的问题。我们依旧是二分答案g。然后就是按照网上其他人的建图方案做的。

采用0-1整数规划的思想来求最优解。使用二分查找的方法来求g(h), 初始left = 0, right = m。对于每一个g=(left+right)/2可以建立一个新图。在新图中源点与原图中每一个点连一条容量为m的边。原图中每一条边与汇点连一条容量为m+2*g-dv的边。dv为点v的度数。再将原图中的无向边拆成两条有向边,容量都设为1.然后对此图求最大流(maxflow)。最后将(n*m-maxflow)/2 与0比较大小,如果它大于0,则left=g,否则right = g。” 

最后从s出发搜索(如果有流量往前搜),能搜到的标号在1到n之间的点就是选择的点的方案。


#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 201, M = 10100;
const double INF = 10000000000;

struct EDGE
 {
     int u, v, next;
     double cap;
 }edge[M];

int map[M][2], n, m, dis[N], cur[N], gap[N], pre[N];
int num, head[N], d[N], step, path[N], s, t;
bool used[N];

void init()
 {
     memset(d, 0, sizeof(d));
     for (int i=1; i<=m; i++)
      {
          scanf("%d%d", &map[i][0], &map[i][1]);
          d[map[i][0]]++;
          d[map[i][1]]++;
      }
 }

void add(int u, int v, double w)
 {
     edge[num].u = u;
     edge[num].v = v;
     edge[num].cap = w;
     edge[num].next = head[u];
     head[u] = num++;
 }

double SAP(int s, int t)
 {
     memset(gap,0,sizeof(gap));
     memset(dis,0,sizeof(dis));
     int i;
     for(int i = 1;i <= t;i++)
      cur[i] = head[i];
     int top = s;
     gap[s] = t;
     double maxflow = 0,flow = INF;
     while(dis[s] < t)
      {
          for(i = head[top];i != -1;i = edge[i].next)
           {
               if(edge[i].cap > 0&& dis[top] == dis[edge[i].v] + 1)
                 break;
           }
          if(i != -1)
           {
               cur[top] = i;
               int v = edge[i].v;
               if(edge[i].cap < flow)
                flow = edge[i].cap;
               top = v;
               pre[v] = i;
               if(top == t)
               {
                   maxflow += flow;
                   while(top != s)
                    {
                        edge[pre[top]].cap -= flow;
                        edge[pre[top]^1].cap += flow;
                        top = edge[pre[top]^1].v;
                    }
                    flow = INF;
               }
           }
          else
          {
            if(--gap[dis[top]] == 0)
             break;
            dis[top] = t;
            cur[top] = head[top];
            for(int j = head[top];j != -1;j = edge[j].next)
             {
                 if(edge[j].cap > 0&& dis[edge[j].v] + 1 < dis[top])
                  {
                      dis[top] = dis[edge[j].v] + 1;
                      cur[top] = j;
                  }
             }
            gap[dis[top]]++;
            if(top != s)
             {
                 top = edge[pre[top]^1].v;
             }
          }
      }
    return maxflow;
 }

double makegraph(double g)
 {
     num = 0;
     memset(head, -1, sizeof(head));
     for(int i=1; i<=n; i++)
      {
          add(s, i, m);
          add(i, s, 0);
          add(i, t, m+2*g-d[i]);
          add(t, i, 0);
      }
     for(int i=1; i<=m; i++)
      {
          add(map[i][0], map[i][1] , 1);
          add(map[i][1], map[i][0] , 0);

          add(map[i][1], map[i][0] , 1);
          add(map[i][0], map[i][1] , 0);
      }
     return n*m - SAP(s, t);
 }

void dfs(int u)
 {
     used[u] = 1;
     if(u>=1 && u<=n)
      path[++step] = u;
     int v;
     for(int i=head[u]; i!=-1; i=edge[i].next)
      {
          v = edge[i].v;
          if(edge[i].cap >0 && !used[v])
           dfs(v);
      }
 }

void solve()
 {
     if(m == 0)
      {
          printf("1\n1\n");
          return ;
      }
     double left=0, right=m, mid, esp = 1.0/n/n/2;
     double now;
     s = n+1;
     t = n+2;
     while(right-left > esp)
      {
          mid = (left+right)/2;
          now = makegraph(mid);
          if(now > 0)
           left = mid;
          else right = mid;
      }
     makegraph(left);
     memset(used, 0, sizeof(used));
     step = 0;
     dfs(s);
     printf("%d\n", step);
     sort(path+1, path+step+1);
     for(int i=1; i<=step; i++)
      printf("%d\n", path[i]);
 }

int main()
 {
     while(scanf("%d%d", &n, &m) != EOF)
     {
         init();
         solve();
     }
    return 0;
 }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值