【GDKOI2016】寻宝Code&Details

Details

模板题,没有太多细节。
如果需要详细的最大权闭合子图解释,请跳转最大权闭合子图和一道更裸的题
ZJOJ上必须要精确到1e-6才能过。
这题因为要求的是最小权,所以把那些值变为相反数再连边。
还有流量为0可能也是合法的,这个要注意。
其实题目有环的话,那些点是要删掉的,可以用tarjan来判断一下再把那些点给删掉,但是当时并不会打,OJ上的数据并没有环的情况然后就过了,也难得去改。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#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(head<tail){
        now=data[++head];
        for(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!");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值