poj 1180:Batch Scheduling【斜率优化dp】

我会斜率优化了!这篇讲的超级棒https://blog.csdn.net/shiyongyang/article/details/78299894?readlog
首先列个n方递推,设sf是f的前缀和,st是t的前缀和:

\[f[i]=min(f[j]+s*(sf[n]-sf[j])+st[i]*(sf[i]-sf[j])) \]

然后移项:

\[f[i]=f[j]+s*sf[n]-s*sf[j]+st[i]*sf[i]-st[i]*sf[j] \]

\[f[i]=f[j]+s*sf[n]+st[i]*sf[i]-s*sf[j]-st[i]*sf[j] \]

\[f[i]=f[j]+s*sf[n]+st[i]*sf[i]-sf[j]*(s+st[i]) \]

\[f[i]+sf[j]*(s+st[i])=f[j]+s*sf[n]+st[i]*sf[i] \]

然后看成斜率表达式b+kx=y,那么

\[b=f[i],x=sf[j],k=(s+st[i]),y=f[j]+s*sf[n]+st[i]*sf[i] \]

那么对于每个i都看成一个点(sf[i],f[j]+s*sf[n]+st[i]*sf[i]),然后在队列里维护斜率单调递增的点(下凸壳),考虑对于当前的i的斜率k,需要在下凸壳上找一个j点使得f[i],也就是b(截距)最小。
显然要找最后一条比k斜率小的点的右端点
然后用斜率去卡队首即可,加点的时候用斜率判断是否是下凸壳,否则去队尾

#include<iostream>
#include<cstdio>
using namespace std;
const long long N=10005,inf=1e18;
long long n,s,st[N],sf[N],f[N],q[N],l,r;
long long read()
{
	long long r=0,f=1;
	char p=getchar();
	while(p>'9'||p<'0')
	{
		if(p=='-')
			f=-1;
		p=getchar();
	}
	while(p>='0'&&p<='9')
	{
		r=r*10+p-48;
		p=getchar();
	}
	return r*f;
}
double wk(int j,int k)
{
	return (double)((double)f[j]-(double)f[k])/(double)((double)sf[j]-(double)sf[k]);
}
int main()
{
	n=read(),s=read();
	for(int i=1;i<=n;i++)
		st[i]=st[i-1]+read(),sf[i]=sf[i-1]+read();
	// for(int i=1;i<=n;i++)
		// cerr<<st[i]<<" "<<sf[i]<<endl;
	for(int i=1;i<=n;i++)
	{//cerr<<wk(q[l+1],q[l])<<"   "<<s+st[i]<<endl;
		while(l<r&&wk(q[l+1],q[l])<s+st[i])
			l++;
		f[i]=f[q[l]]+s*(sf[n]-sf[q[l]])+st[i]*(sf[i]-sf[q[l]]);//cerr<<q[l]<<" "<<f[i]<<endl;
		// for(int j=l;j<=r;j++)
			// cerr<<q[j]<<" ";
		// cerr<<endl;
		while(l<r&&wk(i,q[r])<wk(q[r],q[r-1]))
			r--;
		q[++r]=i;
	}
	printf("%lld\n",f[n]);
	return 0;
}
posted @ 2018-05-26 11:55  lokiii  阅读(135)  评论(0编辑  收藏  举报