[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-线性规划-最大费用最大流的主要内容,如果未能解决你的问题,请参考以下文章