最大密度子图
定义一个无向图
G
=
(
V
,
E
)
G=(V,E)
G=(V,E)的密度
D
=
∣
E
∣
∣
V
∣
D=\frac{|E|}{|V|}
D=∣V∣∣E∣。
给出一个无向图
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=∣V∣∣E∣。子图的就是选这条边的时候一定会选这个边的两个点!
解法:
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+n21的子图,则G’ 是 最 大 密 度 子 图 是最大密度子图 是最大密度子图
看到平均数想到01分数规划。二分答案
m
i
d
mid
mid,那么问题转化为判定是否存在一个子图,
∣
E
∣
−
m
i
d
⋅
∣
V
∣
>
0
|E|−mid⋅|V|>0
∣E∣−mid⋅∣V∣>0.那么可以把每条边的权看成1,每个点的权看成
−
m
i
d
−mid
−mid,而且子图的就是选这条边的时候一定会选这个边的两个点!
这里就非常的像最大权值密度子图啦!!
把边看成点,权值是正的,点的权值是 − m i d -mid −mid按照最大权值闭合子图去建图就可以啦!!
原点连接正权边
汇点连接负权边
里面必选的关系连接INF的流量
答案就是
正
权
值
之
和
−
最
大
流
正权值之和-最大流
正权值之和−最大流
子图的就是选这条边的时候一定会选这个边的两个点!
题目大意:
这是一道裸题
直接建图就可以了但是有一点就是它要输出你选的是那些点?
选择点就是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;
}