[GDKOI2016]寻宝

题目描述

Proverbs 是一个伟大的探险家,他喜欢到各种各样的地方探险,也热衷于在他探险的地方收集宝藏。这一次他来到了一个上古遗迹,于是proverbs 的寻宝之旅就开始了。
这个遗迹里由N 个密室,想要进每个密室都有一些条件,需要打开一些锁,或是要解一些谜题(至于为什么会有多个,你可以认为它的门是多重的,全开了才可以进去)。但是这些钥匙,或是谜题的答案都在其他密室里,于是他就要先到这些密室(这些谜题太难了,或是太奇葩了,又或proverbs 太蠢了,他不可能通过猜答案来通过,他也不会撬锁,因为他从未有过当盗贼的天赋),于是乎就遇到一些问题。
proverbs 要进a 号密室需要放在b 号密室的钥匙,要进b 号密室需要放在a 号密室的谜题答案,那么这两个密室都进不去了,这对proverbs 来说真是个巨大的打击,但还有更可惜的,有的密室不仅没有财宝,还有各种奇怪的怪物,当然有财宝的密室也可能会有怪物镇守。这些怪物会和proverbs 战斗,proverbs 必须战胜他们,于是proverbs 就会受到伤害,这自然是他不想遇到的(除非他是变态暴力狂,我们这里假设他不是)。对于那些没有锁和谜题的密室,proverbs 就可以直接进去了。
现在proverbs 想知道他每得到一点财富值(财富可以用数字度量)需要受到的最小的伤害值(proverbs 受到的伤害可以数字化,不要问为什么)。如果不可能得到宝藏,那么只要得出这个结论就可以了。现在proverbs有幸得到了遗迹的各种信息,但他并不会解决这个问题,于是就找到了他的好朋友你。

最大权闭合图

01分数规划后直接跑就好了……
不知道什么事最大权闭合图可以看我的blog

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<deque>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef double db;
const int maxn=1000+10,maxm=25000+10;
const db inf=100000000,eps=0.000001;
deque<int> dl;
int h[maxn],now[maxn],d[maxn],ru[maxn],go[maxm],fx[maxm],next[maxm];
bool bz[maxn],pp[maxn][maxn];
db dis[maxm],a[maxn],b[maxn];
int i,j,k,s,t,n,m,tot;
db l,r,mid;
void add(int x,int y,db z,int d){
    go[++tot]=y;
    dis[tot]=z;
    fx[tot]=tot+d;
    next[tot]=h[x];
    h[x]=tot;
}
bool zero(db x){
    return x<eps;
}
db dfs(int x,db flow){
    bz[x]=1;
    if (x==t) return flow;
    int r=now[x];
    db k;
    while (r){
        if (!bz[go[r]]&&!zero(dis[r])&&d[x]==d[go[r]]+1){
            k=dfs(go[r],min(flow,dis[r]));
            if (!zero(k)){
                dis[r]=dis[r]-k;
                dis[fx[r]]=dis[fx[r]]+k;
                now[x]=r;
                return k;
            }
        }
        r=next[r];
    }
    now[x]=0;
    return 0;
}
bool change(){
    int tmp=100000000,i,r;
    fo(i,s,t)
        if (bz[i]){
            r=h[i];
            while (r){
                if (!bz[go[r]]&&!zero(dis[r])&&d[go[r]]+1-d[i]<tmp) tmp=d[go[r]]+1-d[i];
                r=next[r];
            }
        }
    if (tmp==100000000) return 0;
    fo(i,s,t) 
        if (bz[i]) d[i]+=tmp;
    return 1;
}
bool check(db ans){
    db x,y=0;
    tot=0;
    fill(h+s,h+t+1,0);
    fill(d+s,d+t+1,0);
    int i,j;
    fo(i,1,n)
        fo(j,1,n)
            if (pp[i][j]){
                add(i+1,j+1,inf,1);
                add(j+1,i+1,0,-1);
            }
    fo(i,1,n){
        x=a[i]-b[i]*ans;
        if (x>-eps){
            y=y+x;
            add(s,i+1,x,1);
            add(i+1,s,0,-1);
        }
        else{
            add(i+1,t,-x,1);
            add(t,i+1,0,-1);
        }
    }
    db tmp=0,k;
    do{
        fo(i,s,t) now[i]=h[i];
        fill(bz+s,bz+t+1,0);
        while (!zero(k=dfs(s,inf))){
            tmp=tmp+k;
            fill(bz+s,bz+t+1,0);
        }
    }while (change());
    tmp=y-tmp;
    return (tmp>eps);
}
bool czy(){
    bool p=0;
    int now;
    fo(i,1,n) 
        if (!ru[i]) dl.push_back(i);
    while (!dl.empty()){
        now=dl.front();
        dl.pop_front();
        bz[now]=1;
        fo(i,1,n)
            if (pp[i][now]){
                ru[i]--;
                if (!ru[i]) dl.push_back(i);
            }
    }
    p=1;
    fo(i,1,n)
        if (bz[i]&&!(fabs(a[i])<eps)){
            p=0;
            break;
        }
    return p;
}
int main(){
    scanf("%d",&n);
    s=1;t=n+2;
    fo(i,1,n){
        scanf("%d",&k);
        while (k--){
            scanf("%d",&j);
            pp[i][j]=1;
            ru[i]++;
        }
    }
    fo(i,1,n) 
        scanf("%lf%lf",&a[i],&b[i]);
    if (czy()){
        printf("CanNotFindTreasure!\n");
        return 0;
    }
    l=0;r=10000;
    while (r-l>eps){
        mid=(l+r)/2;
        if (check(mid)) l=mid;else r=mid;
    }
    printf("%.6lf\n",1/l);
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值