#最大密度子圖,0/1分數規劃#UVA1389 Hard Life
阿新 • • 發佈:2022-03-02
最大密度子圖,0/1分數規劃
題目
\(n\) 個點,\(m\) 條邊的一個無向圖,問匯出子圖的邊數除以點數的最大值
分析
考慮二分這個答案,也就是0/1分數規劃之後轉換成 \(E-mid*V>0\)
這個問題雖然可以精確到具體的最小割,但是也可以泛化到一般的問題。
可以發現轉換成最大權閉合子圖,源點連邊,匯點連點,邊和點之間連 \(inf\)。
然後最後就是在殘餘網路上記錄哪些點可以被訪問,就是匯出子圖的點。
所以最大密度子圖的問題都可以轉化成最大權閉合子圖吧。
程式碼
#include <iostream> #include <queue> using namespace std; const int N=1111; typedef double db; struct node{int y; db w; int next;}e[N<<3]; int dis[N],as[N],et=1,X[N],tot,Y[N],v[N],mark[N],b[N],n,m,S,T; void add(int x,int y,db w){ e[++et]=(node){y,w,as[x]},as[x]=et; e[++et]=(node){x,0,as[y]},as[y]=et; } bool bfs(int st){ for (int i=1;i<=T;++i) dis[i]=0; queue<int>q; q.push(st),dis[st]=1; while (!q.empty()){ int x=q.front(); q.pop(); for (int i=as[x];i;i=e[i].next) if (e[i].w>0&&!dis[e[i].y]){ dis[e[i].y]=dis[x]+1; if (e[i].y==T) return 1; q.push(e[i].y); } } return 0; } db min(db a,db b){return a<b?a:b;} db dfs(int x,db now){ if (x==T||!now) return now; db rest=0,f; for (int i=as[x];i;i=e[i].next) if (e[i].w>0&&dis[e[i].y]==dis[x]+1){ f=dfs(e[i].y,min(now-rest,e[i].w)), rest+=f,e[i].w-=f,e[i^1].w+=f; if (now==rest) return now; } if (!rest) dis[x]=0; return rest; } bool check(db mid){ for (int i=2;i<=et;i+=2) e[i].w+=e[i^1].w,e[i^1].w=0; for (int i=1;i<=n;++i) e[mark[i]].w=mid; db ans=m; while (bfs(S)) ans-=dfs(S,1e9); return ans>1e-8; } void Dfs(int x){ v[x]=1; for (int i=as[x];i;i=e[i].next) if (!v[e[i].y]&&e[i].w) Dfs(e[i].y); } int main(){ ios::sync_with_stdio(0); while (cin>>n>>m){ if (!m) {cout<<"1\n1\n\n"; continue;} for (int i=1;i<=m;++i) cin>>X[i]>>Y[i]; S=n+m+1,T=S+1,et=1; for (int i=1;i<=m;++i) add(S,i+n,1); for (int i=1;i<=n;++i) add(i,T,0),mark[i]=et-1; for (int i=1;i<=m;++i) add(i+n,X[i],1e9),add(i+n,Y[i],1e9); db l=1.0/n,r=m,eps=1.0/(n*n); while (l+eps<r){ db mid=(l+r)/2; if (check(mid)) l=mid; else r=mid; } check(l),Dfs(S),tot=0; for (int i=1;i<=n;++i) if (v[i]) b[++tot]=i; cout<<tot<<endl; for (int i=1;i<=tot;++i) cout<<b[i]<<endl; cout<<endl; for (int i=1;i<=T;++i) as[i]=v[i]=0; } return 0; }