[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$

那么有:

$\text{minimize}$\ \ \ \ $ c^T x$
$\text{S.T.}$ \ \ \ \ \ \ \ \ $Ax \geq b$
\ \ \ \ \ \ \ \ \ \ \ \ $ -x \geq -t$

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

$\text{maximize}$\ \ \ \ $ bTy-tTz $
$\text{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\n",ans);
	return 0;
}
posted @ 2018-07-09 23:33  zltttt  阅读(287)  评论(0)    收藏  举报