最小割 ---- 最大密度子图(最大权值闭合子图)

最大密度子图

定义一个无向图 G = ( V , E ) G=(V,E) G=(V,E)的密度 D = ∣ E ∣ ∣ V ∣ D=\frac{|E|}{|V|} D=VE
给出一个无向图 G = ( V , E ) G=(V,E) G=(V,E),其具有最大密度子图 G ′ = ( V ′ , E ′ ) G'=(V',E') G=(V,E)称为最大密度子图,就是最大化 D = ∣ E ∣ ∣ V ∣ D=\frac{|E|}{|V|} D=VE。子图的就是选这条边的时候一定会选这个边的两个点!


解法:


step1.二分

二分答案g, 下界是 1 n \frac{1}{n} n1,上界为 m m m,且存在下面的引理:

  • 任意两个不同的密度子图的 G 1 , G 2 G1,G2 G1,G2的密度差 > = 1 n 2 >=\frac{1}{n^2} >=n21
  • 因此若对于无向图 G G G中有一个密度为 D D D的子图 G ′ G' G,且不存在一个密度超过 D + 1 n 2 的 子 图 , 则 D+\frac{1}{n^2}的子图,则 D+n21G’ 是 最 大 密 度 子 图 是最大密度子图

看到平均数想到01分数规划。二分答案 m i d mid mid,那么问题转化为判定是否存在一个子图, ∣ E ∣ − m i d ⋅ ∣ V ∣ > 0 |E|−mid⋅|V|>0 EmidV>0.那么可以把每条边的权看成1,每个点的权看成 − m i d −mid mid,而且子图的就是选这条边的时候一定会选这个边的两个点!
这里就非常的像最大权值密度子图啦!!


把边看成点,权值是正的,点的权值是 − m i d -mid mid按照最大权值闭合子图去建图就可以啦!!

原点连接正权边
汇点连接负权边

里面必选的关系连接INF的流量

答案就是 正 权 值 之 和 − 最 大 流 正权值之和-最大流
子图的就是选这条边的时候一定会选这个边的两个点!


POJ3155


题目大意:

这是一道裸题
直接建图就可以了但是有一点就是它要输出你选的是那些点?
选择点就是S集合里面的点,那么就是从S出发能访问到的点就是你要选择的点?

#include <iostream>
#include <queue>
#include <cstring>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-7;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
struct node {
   int to, next;
   double len; 
}e[maxn];
int head[maxn], cnt;
int n, m, s, t;
inline void add(int from, int to, double len) {
   e[cnt] = {to,head[from],len};
   head[from] = cnt ++;
}

int d[maxn];
int cur[maxn];

bool bfs() {
   ms(d,0);
   queue<int> q;
   q.push(s); d[s] = 1;
   while(!q.empty()) {
      int u = q.front(); q.pop();
      for(int i = head[u]; ~i; i = e[i].next) {
         int v = e[i].to;
         if(d[v] || e[i].len <= 0.0) continue;
         q.push(v);
         d[v] = d[u] + 1;
      }

   }      
   for(int i = 0; i <= t; ++ i) cur[i] = head[i];
   return d[t] != 0;
}

double dfs(int u, double flow) {
    if(u == t) return flow;
    for(int &i = cur[u]; ~i; i = e[i].next) {
        int v = e[i].to;
        if(d[u] + 1 != d[v] || e[i].len <= 0) continue;
        double delta = dfs(v,min(flow,e[i].len));
        if(delta <= 0) continue;
        e[i].len -= delta;
        e[i^1].len += delta;
        return delta;
    }
    return 0.0;
}

double get_maxflow() {
    double maxFlow = 0.0, delta;
    while(bfs())//bfs进行构建最短路网络
         while(delta = dfs(s,1e9))
             maxFlow += delta;
    return maxFlow;
}
PII edge[maxn];

inline void build(double Mid) {
   ms(head,-1);
   cnt = 0;
   s = 0, t = n + m + 1;
   for(int i = 1; i <= m; ++ i) {
      int u = edge[i].first, v = edge[i].second;
      
      add(s,i+n,1);
      add(i+n,s,0);

      add(i+n,u,1e9);
      add(u,i+n,0);

      add(i+n,v,1e9);
      add(v,i+n,0);

   }
   for(int i = 1; i <= n; ++ i) {
      add(i,t,Mid);
      add(t,i,0);
   }

}
bool vis[maxn];
int Count = 0;
void dfs1(int x){
    vis[x]=1;
    if(x>=1&&x<=n)Count++;
    for(int i=head[x];~i;i=e[i].next){
        int v=e[i].to;
        if(e[i].len&&!vis[v])dfs1(v);
    }
}

int main() {
   //IOS;
   cin >> n >> m;
   for(int i = 1; i <= m; ++ i) {
      int u, v;
      cin >> u >> v;
      edge[i] = {u,v};
   }
   if(!m) {
      cout << "1\n1\n";
      return 0;
   }
   double  l = 1.0/n, r = m;
   while(r - l > eps) {
      double Mid = (l + r) / 2.0;
      build(Mid);
      if(1.0 * m - get_maxflow() > eps) l = Mid;
      else r = Mid; 
   }
   // cout << fixed << " " << setprecision(10) << l << endl;
   // cout << fixed << " " << setprecision(10) << 5.0/4.0 << endl;
   build(l);
   get_maxflow();
   dfs1(s);
   cout << Count << endl;
   for(int i = 1; i <= n; ++ i)
      if(vis[i])
        cout << i << endl;
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值