[2018.6.23集训]B-线性规划-最大费用最大流

Posted zltttt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[2018.6.23集训]B-线性规划-最大费用最大流相关的知识,希望对你有一定的参考价值。

题目大意

给定 $n$ 个待安装软件包。每一个软件包安装所需时间为 $t_i$。
软件包之间的依赖关系构成了一张 $n$ 个点 $m$ 条边的DAG,一条有向边 $(u->v)$ 代表 $v$ 依赖于 $u$ 。无依赖关系的软件包可以同时安装。
对于每个软件包,可以花费$c_i$的时间让其 $t_i$ 降低 $1$,可以多次减但不能减为负。
给定预算 $w$ ,求出安装完这些软件包所需的最短时间。

$1 leq n leq 55 , 1 leq m leq 400 , 1 leq t_i leq 10^3 , 1 leq c_i leq 10^4, 1 leq w leq 10^9$

题解

看着有种网络流的气息。

考虑二分最后的答案 $ans$,将问题转化为达成当前答案最少需要花费的金额。
那么可以将问题描述成下列一个线性规划问题:

设:
$x_i$ : $i$ 号点被减少的时间
$A_ij$ : $j$ 号点是否在 $i$ 号路径上(路径数可能是指数级的)
$b_i$ : $i$ 号路径的长度减去 $ans$

那么有:

$ ext{minimize}$???? $ c^T x$
$ ext{S.T.}$ ???????? $Ax geq b$
? ? ? ? ????????$ -x geq -t$

看不出什么问题。
那么对偶一波。

$ ext{maximize}$???? $ b^Ty-t^Tz $
$ ext{S.T.}$ ????????????$ A^Ty -z leq c$

考虑上面的线性规划的含义。
这意味着,定义原图中一条路径的收益为其长度减去二分的 $ans$ ,一条路径会覆盖所有其经过点,当一个节点被覆盖超过 $c_i$ 次,需要额外支付 $t_i$ 的代价,最大化最后的收益。

于是拆点跑费用流即可~

代码:

#include<cstdio>
using namespace std;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x*f;
}

const int N=59;
const int M=409;
const int Inf=1e9+7;

inline bool chkmin(int &a,int b){if(a>b)return a=b,1;return 0;}
inline bool chkmax(int &a,int b){if(a<b)return a=b,1;return 0;}
inline int min(int a,int b){return a<b?a:b;}

int n,m,w,s,t;
int u[M],v[M];
int ti[N],c[N];

namespace flow
{
    const int P=N*2;
    const int E=P*P*2;

    int to[E],nxt[E],w[E],cost[E],beg[P],tot;
    int dis[P],q[P],inq[P],mul,ans;

    inline void init()
    {
        for(int i=1;i<=t;i++)
            beg[i]=0;
        tot=1;ans=0;
    }

    inline void adde(int u,int v,int c,int d)
    {
        to[++tot]=v;
        nxt[tot]=beg[u];
        w[tot]=c;
        cost[tot]=d;
        beg[u]=tot;
    }

    inline void add(int u,int v,int c,int d)
    {
        adde(u,v,c,d);adde(v,u,0,-d);
    }

    inline int nxtv(int x){return (x+1)%(P-5);}

    inline bool spfa()
    {
        for(int i=1;i<=t;i++)
            dis[i]=-Inf;
        dis[t]=0;q[0]=t;
        for(int l=0,r=1,u=q[l];l!=r;u=q[l=nxtv(l)],inq[u]=0)
            for(int i=beg[u];i;i=nxt[i])
                if(w[i^1]>0 && chkmax(dis[to[i]],dis[u]+cost[i^1]) && !inq[to[i]])
                    inq[to[i]]=1,q[r]=to[i],r=nxtv(r);
        return (mul=dis[s])!=Inf;
    }

    inline int dfs(int u,int flow)
    {
        if(u==t || !flow)return ans+=mul*flow,flow;
        int costs=0,cdis=dis[u];dis[u]=Inf;
        for(int i=beg[u],f;i;i=nxt[i])
            if(w[i]>0 && dis[to[i]]!=Inf && cdis==dis[to[i]]+cost[i])
            {
                f=dfs(to[i],min(w[i],flow-costs));
                w[i]-=f;w[i^1]+=f;costs+=f;
                if(costs==flow)break;
            }
        return costs;
    }

    inline int lim_mcmf()
    {
        while(spfa() && mul>=0)
            while(dfs(s,Inf));
        return ans;
    }

    inline int mcmf()
    {
        while(spfa())
            while(dfs(s,Inf));
        return ans;
    }
}

inline bool check(int x)
{
    flow::init();
    
    for(int i=1;i<=n;i++)
    {
        flow::add(i*2-1,i*2,c[i],ti[i]);
        flow::add(i*2-1,i*2,Inf,0);
        flow::add(s,i*2-1,Inf,0);
        flow::add(i*2,t,Inf,-x);
    }
    for(int i=1;i<=m;i++)
        flow::add(u[i]*2,v[i]*2-1,Inf,0);

    return flow::mcmf()<=w;
}

int main()
{
    n=read();m=read();w=read();
    for(int i=1;i<=n;i++)
        ti[i]=read();
    for(int i=1;i<=n;i++)
        c[i]=read();
    for(int i=1;i<=m;i++)
    {
        u[i]=read();
        v[i]=read();
    }

    s=n*2+1;t=s+1;
    int l=0,r=1e5,ans=1e9;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))
            ans=mid,r=mid-1;
        else
            l=mid+1;
    }

    printf("%d
",ans);
    return 0;
}

以上是关于[2018.6.23集训]B-线性规划-最大费用最大流的主要内容,如果未能解决你的问题,请参考以下文章

循环数组最大子段和(动态规划思想的巧妙转换)

五月集训(第28天) —— 动态规划

答案解析第四章:动态规划

bzoj1283序列 线性规划与费用流

省队集训 Day3 吴清华

线性基 uoj 36 清华集训2014 玛里苟斯