【POJ3155】Hard Life-01分數規劃+最小割
阿新 • • 發佈:2019-02-16
測試地址:Hard Life
題目大意:有一個無向圖,要從裡面選出一個子圖,使得邊數和點數的比最大,輸出一個合法方案。
做法:本題需要用到01分數規劃+最小割。
首先要求比值最大,我們立刻想到01分數規劃的套路,二分比值,這樣就變成判定性問題:存不存在一個子圖使得。
怎麼樣選出一個合法的子圖?注意到,如果我們選了一條邊,那麼這條邊的兩個端點必須全部選上,而且選一條邊的收益是,選一個點的成本是。要求收益最大的話,這顯然就是一個最大權閉合子圖的模型了。求出最大流後,沿著殘餘網路能到達的點就是我們選擇的子圖的邊和點了。
這題有一個非常噁心的地方就是精度。因為你在二分的時候,左右端點無限逼近最優點,但只要稍微偏移一點,就根本不存在方案,所以我們令出二分後的左端點為 ,我們應該令再做一次最大流,這樣就能保證一定能取到一個方案。注意上述問題後,再特判答案為的情況(即)即可。
以下是本人程式碼:
#include <bits/stdc++.h>
using namespace std;
const double inf=1000000000.0;
const double eps=1e-8;
int n,m,S,T,first[2010]={0},tot=1,last;
int lvl[2010],cur[2010],h,t,q[2010];
int cnt,ans[2010];
bool vis[2010]={0};
struct edge
{
int v,next;
double f;
}e[50010];
void insert(int a,int b,double f)
{
e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,first[a]=tot;
e[++tot].v=a,e[tot].next=first[b],e[tot].f=0.0,first[b]=tot;
}
void init()
{
scanf("%d%d",&n,&m);
S=n+m+1,T=n+m+2;
for(int i=1;i<=m;i++)
{
int a,b;
insert(S,n+i,1.0);
scanf("%d%d",&a,&b);
insert(n+i,a,inf);
insert(n+i,b,inf);
}
last=tot;
for(int i=1;i<=n;i++)
insert(i,T,0.0);
}
bool makelevel()
{
for(int i=1;i<=T;i++)
lvl[i]=-1,cur[i]=first[i];
lvl[S]=0;
h=t=1;
q[1]=S;
while(h<=t)
{
int v=q[h++];
for(int i=first[v];i;i=e[i].next)
if (e[i].f>eps&&lvl[e[i].v]==-1)
{
lvl[e[i].v]=lvl[v]+1;
q[++t]=e[i].v;
}
}
return lvl[T]!=-1;
}
double maxflow(int v,double maxf)
{
double ret=0.0,f;
if (v==T) return maxf;
for(int i=cur[v];i;i=e[i].next)
{
if (e[i].f>eps&&lvl[e[i].v]==lvl[v]+1)
{
f=maxflow(e[i].v,min(maxf-ret,e[i].f));
ret+=f;
e[i].f-=f;
e[i^1].f+=f;
if (maxf-ret<eps) break;
}
cur[v]=i;
}
if (ret<eps) lvl[v]=-1;
return ret;
}
bool dinic()
{
double maxf=0.0;
while(makelevel()) maxf+=maxflow(S,inf);
return (double)m-maxf>eps;
}
void findsol()
{
h=t=1;
q[1]=S;
while(h<=t)
{
int v=q[h++];
for(int i=first[v];i;i=e[i].next)
if (e[i].f>eps&&!vis[e[i].v])
{
vis[e[i].v]=1;
q[++t]=e[i].v;
}
}
}
void modify(double x)
{
tot=1;
for(int i=1;i<=m;i++)
{
e[++tot].f=1.0,e[++tot].f=0.0;
e[++tot].f=inf,e[++tot].f=0.0;
e[++tot].f=inf,e[++tot].f=0.0;
}
for(int i=1;i<=n;i++)
e[++tot].f=x,e[++tot].f=0.0;
}
void work()
{
if (!m) {printf("1\n1");return;}
double l=0.0,r=(double)m;
while(r-l>=eps)
{
double mid=(l+r)/2.0;
modify(mid);
if (dinic()) l=mid;
else r=mid;
}
modify(l-eps);
dinic();
findsol();
cnt=0;
for(int i=last+1,j=1;i<=tot;i+=2,j++)
if (vis[j]) ans[++cnt]=j;
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
printf("%d\n",ans[i]);
}
int main()
{
init();
work();
return 0;
}