P2120 [ZJOI2007]仓库建设

令 \(f_i\) 表示在 \(i\) 工厂建立仓库,\(i+1\sim n-1\) 工厂不建立仓库的最小总费用。对 \(p\) 做一遍前缀和。

转移直接枚举上一个建立的工厂在哪儿:

\[f_i=f_j-(p_i-p_j)\times(x_n-x_i)(j<i)+c_i \]

考虑两个决策 \(j,k(j<k)\),若 \(j\) 比 \(k\) 优:

\[f_j-(p_i-p_j)\times(x_n-x_i)<f_k-(p_i-p_k)\times(x_n-x_i) \]

\[f_j-f_k<(x_n-x_i)\times(p_i-p_j-p_i+p_k) \]

\[\dfrac{f_k-f_j}{p_k-p_j}>x_i-x_n \]

因为 \(x_i-x_n\) 随着 \(i\) 的增大单调递增,所以维护一个下凸壳。

时间复杂度 \(O\left(n\right)\)。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 1000005
#define Db double
#define Min(x,y)((x)<(y)?x:y)
#define For(i,x,y)for(i=x;i<=(y);i++)
ll p[N],f[N];
int x[N],c[N],que[N];
int read()
{
	int A;
	bool K;
	char C;
	C=A=K=0;
	while(C<'0'||C>'9')K|=C=='-',C=getchar();
	while(C>'/'&&C<':')A=(A<<3)+(A<<1)+(C^48),C=getchar();
	return(K?-A:A);
}
inline Db slope(int j,int k)
{
	return Db(f[k]-f[j])/Db(p[k]-p[j]);
}
int main()
{
	int n,i,head,tail;
	n=read();
	For(i,1,n)
	{
		x[i]=read(),p[i]=read(),c[i]=read();
		f[0]-=p[i]*x[i];
		p[i]+=p[i-1];
	}
	f[0]+=p[n]*x[n];
	head=tail=1;
	For(i,1,n-1)
	{
		while(head<tail&&slope(que[head],que[head+1])<=x[i]-x[n])head++;
		f[i]=f[que[head]]-(p[i]-p[que[head]])*(x[n]-x[i])+c[i];
		/*cout<<i<<' '<<f[i]<<' '<<que[head]<<endl;*/
		while(head<tail&&slope(que[tail-1],que[tail])>=slope(que[tail],i))tail--;
		que[++tail]=i;
	}
	For(i,1,n-1)f[0]=Min(f[0],f[i]);
	cout<<f[0]+c[n];
	return 0;
}
上一篇:数据结构课设——停车场管理


下一篇:算法整理 & 复习:拓扑排序