UVA1389 Hard Life
题意
给定一张无向图 \(G\),求它的一个导出子图 \(G'=\{V,E\}\),使得 \(\dfrac{|E|}{|V|}\) 最大。
Solution
求分数的最大值,首先想到分数规划。按套路二分当前的答案 \(mid\),那么有:
\[\dfrac{|E|}{|V|}>mid\Leftrightarrow |E|>mid|V|\Leftrightarrow |E|-mid|V|>0
\]
考虑导出子图的限制。按照定义,如果一条边在子图中,那么两个端点也必然在子图中。对点建点,对边建点,然后边向对应的端点连有向边。此时如果边的权值是 \(1\),点的权值是 \(-mid\),就转化成一个最大权闭合子图的问题。
关于最大权闭合子图
建超级源点和超级汇点,然后源点连向正权点,容量是点权,负权点连向汇点,容量是负点权。原图内的边容量为 \(\infty\)。最大权值和就是正权和减去最小割。
听说这个东西叫做最大密度子图。
Code
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) (a).begin(),(a).end()
#define siz(a) (int)(a).size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
// #define int long long
using namespace std;
const double eps=1e-6;
const int MAXN=2110;
struct Edge{int to;double fl,cp;};
vector<Edge> g;
vector<int> e[MAXN];
void add(int u,int v,double w){
g.pb(Edge{v,0,w});e[u].pb(siz(g)-1);
g.pb(Edge{u,0,0});e[v].pb(siz(g)-1);
}
int S,T,d[MAXN];
bool bfs(){
queue<int> q;q.push(S);
clr(d);d[S]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int s:e[x]){
if(g[s].fl>=g[s].cp) continue;
if(d[g[s].to]) continue;
d[g[s].to]=d[x]+1;
q.push(g[s].to);
if(g[s].to==T) return 1;
}
}return 0;
}
int cur[MAXN];
double dfs(int x,double a){
if(x==T||a<=0) return a;
double fl=0,f;
for(int &i=cur[x];i<siz(e[x]);i++){
int s=e[x][i];
if(d[g[s].to]!=d[x]+1) continue;
if((f=dfs(g[s].to,min(a,g[s].cp-g[s].fl)))<=0) continue;
fl+=f;a-=f;g[s].fl+=f;g[s^1].fl-=f;
if(a<=0) break;
}return fl;
}
int a[MAXN],b[MAXN],n,m;
bool check(double mid){
rep(i,0,n+m+1) e[i].clear();
g.clear();S=n,T=n+m+1;
rep(i,1,m)
add(n+i,a[i],10000000000),
add(n+i,b[i],10000000000);
rep(i,1,n) add(i,T,mid);
rep(i,1,m) add(S,n+i,1);
double sum=0;
while(bfs()){clr(cur);sum+=dfs(S,10000000000);}
return m>sum;
}
bool vis[MAXN];
void getans(int x){
vis[x]=1;
for(int s:e[x]){
if(!vis[g[s].to]&&g[s].fl<g[s].cp)
getans(g[s].to);
}
}
void solve(){
rep(i,1,m) cin>>a[i]>>b[i];
double l=0,r=10000;
while(r-l>eps){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}check(l);
memset(vis,0,sizeof(vis));
getans(S);
vector<int> ans;
rep(i,1,n) if(vis[i]) ans.pb(i);
cout<<siz(ans)<<'\n';
for(int v:ans) cout<<v<<'\n';
cout<<'\n';
rep(i,S,T) e[i].clear();g.clear();
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
while(cin>>n>>m) solve();
return 0;
}