任务安排

有 N 个任务排成一个序列在一台机器上等待执行,它们的顺序不得改变。
机器会把这 N 个任务分成若干批,每一批包含连续的若干个任务。
从时刻 0 开始,任务被分批加工,执行第 i 个任务所需的时间是 Ti。
另外,在每批任务开始前,机器需要 S 的启动时间,故执行一批任务所需的时间是启动时间 S 加上每个任务所需时间之和。
一个任务执行后,将在机器中稍作等待,直至该批任务全部执行完毕。
也就是说,同一批任务将在同一时刻完成。
每个任务的费用是它的完成时刻乘以一个费用系数 Ci。
请为机器规划一个分组方案,使得总费用最小。

\(O(n^3)\)

前i个任务分成j批:

\[f(i,j)=\min_{0\le k < j } \{ f(k,j-1)+(S*j+sum(i))*(sumC(i)-sumC(k)) \} \]

\(O(n^2)\)

由于我们只关注f(n)的正确性 不关注其他 所以我们可以考虑把启动时间造成的影响提前加入到状态中进行计算
这样仍然满足最优子结构:
因为从i转移到j的时候 在j点启动的时间是固定的 最终累加到n的时候是确定的
f的含义可以理解为把前i个任务分成若干批次,并且加上每一批造成的后续影响的最小费用

\[f(i)=\min_{0\le j < i} \{ f(j)+sumT(i)*(sunC(i)-sumC(j) ) +S*(sumC(N)-sumC(j))\} \]

斜率优化

开始进行斜率优化

\[f(j)=(S+sumT(i))*sumC(j)+f(i)-sumT(i)*sumC(i)-S*sumC(n) \]

把i看做是定值

\[f(j)=k(i)*sumC(j)+b(i) \]

单调队列维护上凹壳
进行线形规划 我们有两点前提

  1. c非负,则
    \(j1 \le j2 \le j3\)\(sumC(j1)<sumC(j2)<sumC(j3)\)
    这样新的决策出现在凹壳的最后,胆小队列是一个j递增,斜率递增的队列
  2. t非负
    则可以从对头弹出不优秀的决策 从对头转移
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;

const int N=3e5+10;

int read()
{
	int x=0,f=0,c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return f?-x:x;
}
int n,S;
ll sumT[N],sumC[N],f[N];
int q[N],l,r;

bool cmp_b(int j1,int j2,int j3)// k_i,2 <= k_2,3
{
	return (f[j2]-f[j1])*(sumC[j3]-sumC[j2])
		   >=(f[j3]-f[j2])*(sumC[j2]-sumC[j1]);
}
bool cmp_f(int j1,int j2,int k)// k_1,2<=k
{
	return (f[j2]-f[j1])<=k*(sumC[j2]-sumC[j1]);
}



int main()
{
	n=read(); S=read();
	for(int i=1;i<=n;i++) 
		sumT[i]=sumT[i-1]+read(),sumC[i]=sumC[i-1]+read();
	memset(f,0x3f,sizeof f); f[0]=0;
	l=1;r=1;
	for(int i=1;i<=n;i++)
	{
		while(l<r&& cmp_f(q[l],q[l+1],S+sumT[i]) ) l++;		
		f[i]=f[q[l]]-(S+sumT[i])*sumC[q[l]] + sumT[i]*sumC[i]+S*sumC[n];
		while(l<r&&cmp_b(q[r-1],q[r],i) ) r--;
		q[++r]=i;
	}

	printf("%lld",f[n]);	
	return 0;
	
}

斜率优化__斜率不单调

不能够从对头转移 在单调队列里面二分

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;

const int N=3e5+10;

int read()
{
	int x=0,f=0,c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return f?-x:x;
}
int n,S;
ll sumT[N],sumC[N],f[N];
int q[N],l,r;

bool cmp_b(int j1,int j2,int j3)// k_i,2 <= k_2,3
{
	return (f[j2]-f[j1])*(sumC[j3]-sumC[j2])
		   >=(f[j3]-f[j2])*(sumC[j2]-sumC[j1]);
}
bool cmp_k(int j1,int j2,ll k)// k_1,2<=k
{
	return (f[j2]-f[j1])>=k*(sumC[j2]-sumC[j1]);
}

int find(ll k)//找到斜率>k的第一个点
{
	int L=l-1,R=r;
	while(L+1<R)
	{
		int mid=(L+R)>>1;
		if( cmp_k(q[mid],q[mid+1],k)   )  R=mid;
		else L=mid;
	}
	return q[R];
} 


int main()
{
	n=read(); S=read();
	for(int i=1;i<=n;i++) 
		sumT[i]=sumT[i-1]+read(),sumC[i]=sumC[i-1]+read();
	memset(f,0x3f,sizeof f); f[0]=0;
	l=1;r=1;
	for(int i=1;i<=n;i++)
	{
		int p=find(S+sumT[i]);
		f[i]=f[p]-(S+sumT[i])*sumC[p] + sumT[i]*sumC[i]+S*sumC[n];
		while(l<r&&cmp_b(q[r-1],q[r],i) ) r--;
		q[++r]=i;
	}

	printf("%lld",f[n]);	
	return 0;
	
}

注意分析数据范围 甚至有可能爆longlong

posted @ 2022-03-07 14:57  __iostream  阅读(69)  评论(0)    收藏  举报