模板题,没有太多细节。
如果需要详细的最大权闭合子图解释,请跳转最大权闭合子图和一道更裸的题
ZJOJ上必须要精确到1e-6才能过。
这题因为要求的是最小权,所以把那些值变为相反数再连边。
还有流量为0可能也是合法的,这个要注意。
其实题目有环的话,那些点是要删掉的,可以用tarjan来判断一下再把那些点给删掉,但是当时并不会打,OJ上的数据并没有环的情况然后就过了,也难得去改。
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=30005;
int i,j,k,t,n,m,a[2000][2000];
double l,r,mid,ans,ans1;
int num,first[maxn*3],next[maxn*3],last[maxn*3],fan[maxn*3],b[maxn],c[maxn],data[maxn*3];
double chang[maxn*3];
int d[maxn];
void add(int x,int y,double z){
last[++num]=y;
next[num]=first[x];
first[x]=num;
chang[num]=z;
fan[num]=num+1;
last[++num]=x;
next[num]=first[y];
first[y]=num;
chang[num]=0;
fan[num]=num-1;
}
bool bfs(){
int head=0,tail=1,now,i;
memset(d,0,sizeof(d));
d[0]=1;data[1]=0;
while(headfor(i=first[now];i;i=next[i]){
if(d[last[i]]==0&&chang[i]>0){
d[last[i]]=d[now]+1;
data[++tail]=last[i];
}
}
}
return (d[n+1]!=0);
}
double dinic(int x,double y){
int i;
double j=0,k;
if(x==n+1){
return y;
}
for(i=first[x];i;i=next[i]){
if(chang[i]>0&&d[last[i]]==d[x]+1){
k=dinic(last[i],min(chang[i],y));
if(k>0){
chang[i]-=k;
chang[fan[i]]+=k;
y-=k;j+=k;if(y==0)break;
}
}
}
if(j==0)d[x]=-1;
return j;
}
int main(){
scanf("%d",&n);
fo(i,1,n){
scanf("%d",&a[i][0]);
fo(j,1,a[i][0]){
scanf("%d",&a[i][j]);
// add(i,a[i][j],0x7fffffff);
}
}
fo(i,1,n){
scanf("%d%d",&b[i],&c[i]);
}
l=0;r=10000;
while(r-l>1e-6){
mid=(r+l)/2;
num=0;
ans1=0;
memset(first,0,sizeof(first));
fo(i,1,n){
double p=b[i]*mid-c[i];
if(p>0){
add(0,i,p);
ans1+=p;
}
else if(p<0){
add(i,n+1,-p);
}
}
fo(i,1,n){
fo(j,1,a[i][0]){
add(i,a[i][j],0x7fffffff);
}
}
while(bfs())ans1-=dinic(0,0x7fffffff);
if(ans1>0) r=mid;else l=mid;
}
if(l>1e-6) printf("%.6lf\n",l);
else printf("CanNotFindTreasure!");
}