BZOJ 1096: [ZJOI2007]仓库建设

starli posted @ 2016年2月14日 18:50 in 动态规划 , 156 阅读

推dp方程

首先将所有的工厂按距离排序

定义f[i]表示前i个工厂的所有货都能够保存下来的最小费用,cost[i][j]为把所有i到j的货物运到j的费用,那么显然

$f[i]=min{ f[j]+cost[j+1][i]+c[i]} (0\leq j \leq i-1)$

考虑如何计算cost[i][j],定义sum[i]表示前i个工厂的物品个数之和,d[i]表示工厂i到第一个工厂的距离 那么可以得到

$cost[i][j]=cost[1][j]-cost[1][i]-sum[j]*(d[j]-d[i])$

于是只要预处理出cost[1][i]和sum[i]就可以O(1)计算cost[i][j]。下面用cost[i]表示cost[1][i]

看到n这么大,我们试一试斜率优化

假设j>k,且决策j比k优

那么有

$f[j]+cost[i]-cost[j]-sum[j]*(d[i]-d[j])+c[i] < f[k]+cost[i]-cost[k]-sum[k]*(d[i]-d[k])+c[i]$

将所有有关i的移项到右边其他移到左边可以得到

$(f[j]-cost[j])-(f[k]-cost[k])+sum[j]*d[j]-sum[k]*d[k] < d[i]*(sum[j]-sum[k])$

由于sum[j]>sum[k],可以除过来,否则要考虑不等号变向问题 则

$\frac{(f[j]-cost[j])-(f[k]-cost[k])+sum[j]*d[j]-sum[k]*d[k]}{(sum[j]-sum[k])} < d[i]$ d[i]递增可以斜率优化

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=1000010;
struct Fact{LL c,d,p;}fa[N];
LL n,cost[N],f[N],sum[N],q[N*3],l,r;
bool cmp(Fact a,Fact b){return a.d<b.d;}
double slop(int k,int j){return (f[j]-cost[j]-(f[k]-cost[k])+sum[j]*fa[j].d-sum[k]*fa[k].d)/(double)(sum[j]-sum[k]);}
void read(LL &x){
	char c;
	while((c=getchar())<'0' || c>'9');x=c-'0';
	while((c=getchar())>='0' && c<='9') x=x*10+c-'0';
}
int main(){
	//freopen("1096.in","r",stdin);freopen("1096.out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++) read(fa[i].d),read(fa[i].p),read(fa[i].c);
	sort(fa+1,fa+n+1,cmp);
	for(int i=1;i<=n;i++) sum[i]=sum[i-1]+fa[i].p,cost[i]=cost[i-1]+sum[i-1]*(fa[i].d-fa[i-1].d);
	for(int i=1;i<=n;i++){
		while(l<r && slop(q[l],q[l+1])<fa[i].d) l++;
		int j=q[l];
		f[i]=f[j]+cost[i]-cost[j]-sum[j]*(fa[i].d-fa[j].d)+fa[i].c;
		while(l<r && slop(q[r-1],q[r])>slop(q[r],i)) r--;
		q[++r]=i;
	}
	printf("%lld",f[n]);
	return 0;
}

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter