bzoj 4501 旅行

01分数规划+最大权闭合子图
倒拓扑序处理每个节点
f[x]=f[v]n+1
二分答案 val
只需要判断是否存在 f[v]+1val>0 即可
点权下放给边,限制{x,y}即为若边x存在,则边y存在
建图,跑网络流即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#define inf 0x7fffffff
#define eps 1e-7
using namespace std;


namespace network{
    const int N = 1005;
    const int S = 1001;
    const int T = 1002;
    int e=2,head[N];
    struct edge{
        int u,v,next;
        double f;
    }ed[310000];
    void add(int u,int v,double f){
        ed[e].u=u;ed[e].v=v;ed[e].f=f;
        ed[e].next=head[u];head[u]=e++;
        ed[e].u=v;ed[e].v=u;ed[e].f=0;
        ed[e].next=head[v];head[v]=e++;
    }
    int dep[N];
    int q[N],h,t;
    bool bfs(){
        memset(dep,-1,sizeof dep);
        dep[S]=1; h=t=1; q[1]=S;
        while(h<=t){
            int x=q[h++];
            for(int i=head[x];i;i=ed[i].next){
                if(ed[i].f&&dep[ed[i].v]==-1){
                    dep[ed[i].v]=dep[x]+1;
                    q[++t]=ed[i].v;
                }
            }
        }
        return dep[T]!=-1;
    }
    double dfs(int x,double f){
        if(x==T||f==0)return f;
        double ans=0;
        for(int i=head[x];i;i=ed[i].next){
            if(ed[i].f&&dep[ed[i].v]==dep[x]+1){
                double nxt=dfs(ed[i].v,min(f,ed[i].f));
                ed[i].f-=nxt; ed[i^1].f+=nxt;
                f-=nxt; ans+=nxt;
                if(f==0)break;
            }
        }
        if(ans==0)dep[x]=-1;
        return ans;
    }
    double dinic(){
        double ans=0;
        while(bfs())ans+=dfs(S,inf);
        return ans;
    }
    void init(){memset(head,0,sizeof head);e=2;}
}


namespace graph{
    const int N =505;
    int e=1,head[N],out[N];
    vector<int> lim[N];
    bool vis[N];
    double f[N];
    struct edge{
        int u,v,next;
    }ed[2*N];
    void add(int u,int v){
        ed[e].u=u;ed[e].v=v;
        ed[e].next=head[u];
        head[u]=e++;out[u]++;
    }
    bool check(int x,double y){
        network::init();
        double sum=0;
        for(int i=head[x];i;i=ed[i].next){
            int v=ed[i].v;
            double val=f[v]+1-y;
            sum+=max(val,(double)0);
            if(val>0)network::add(network::S,i,val);
            else network::add(i,network::T,-val);
            for(int j=0;j<lim[i].size();j++)
                network::add(i,lim[i][j],(double)inf);
        }
        return sum-network::dinic()>0;
    }
    double solve(int x){
        if(!out[x])return 0.0;
        double l=0,r=0,mid,ans;
        for(int i=head[x];i;i=ed[i].next)
            r=max(r,f[ed[i].v]+1);
        while(r-l>=eps){
            mid=(l+r)/2.0;
            if(check(x,mid))l=ans=mid;
            else r=mid;
        }
        return ans;
    }
    void work(int x){
        for(int i=head[x];i;i=ed[i].next)
            if(!vis[ed[i].v]){
                work(ed[i].v);
                vis[ed[i].v]=1;
            }
        f[x]=solve(x);
    }
}

int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);
        graph::add(u,v);
    }
    for(int i=1,x,y;i<=k;i++){
        scanf("%d%d",&x,&y);
        graph::lim[x].push_back(y);
    }
    graph::work(1);
    printf("%lf\n",graph::f[1]);
}
/*
3 3 1
1 2
1 3
2 3
1 2
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值