bzoj3597 [Scoi2014]方伯伯运椰子 01分数规划

题意:给你一个满流的残量网络,让你通过调整以后,使得调整前后的平均费用之差(调整后比调整前小)最大,同时要求调整后仍然满流。
其实这题不难,只不过网上的题解大多数都模棱两可,写法也参差不一,搞得我很蛋疼。。
事实上我们可以发现,当前给我们的图肯定不是最小流,否则无法增广。
然后,根据费用流的消圈定理,我们只用增广一个负环就好了,所以用01分数规划一下,然后spfa判断是否有负环。

根据01分数规划,本题目标是:最大化λ=(X-Y)/k
则有f(λ)=λk+Y-X
扩边一次的费用为b[i]+d[i]+λ
缩边一次的费用为a[i]-d[i]+λ
每一次二分的时候重新连一次就好了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=3e5+5;
const double eps=1e-3;
int n,m;
int head[N],go[N],tot,next[N];
double dis[N],val[N];
int vis[N]; 
struct node
{
    int a,b,c,d,u,v;
}e[N];
inline void add(int x,int y,double z)
{
    go[++tot]=y;
    next[tot]=head[x];
    val[tot]=z;
    head[x]=tot;
}
inline bool dfs(int x)
{
    vis[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (dis[x]+val[i]<dis[v])
        {
            if(vis[v])return 1;
            dis[v]=dis[x]+val[i];
            if (dfs(v))return 1;
        }
    }
    vis[x]=0;
    return 0;
}
bool pd(double x)
{
    memset(head,0,sizeof(head));
    tot=0;
    fo(i,1,m)
    {
        add(e[i].u,e[i].v,e[i].b+e[i].d+x);
        if (e[i].c)
        add(e[i].v,e[i].u,e[i].a-e[i].d+x);
    }
    fo(i,1,n+2)dis[i]=vis[i]=0;
    fo(i,1,n+2)if (dfs(i))return 1;
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    fo(i,1,m)
    {
        int u,v,a,b,c,d;
        scanf("%d%d%d%d%d%d",&u,&v,&a,&b,&c,&d);
        e[i].a=a;
        e[i].b=b;
        e[i].c=c;
        e[i].d=d;
        e[i].u=u;
        e[i].v=v;
    }
    double l=0.0,r=1e9;
    while (r-l>eps)
    {
        double mid=(l+r)/2;
        if (pd(mid))l=mid;
        else r=mid;
    }
    printf("%.2lf\n",l);
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值