任务安排

原题链接

序言

本题下面将给出 \(3\) 种做法,分别为:

  • \(O(n^3)\) 暴力算法。
  • \(O(n^2)\) 费用提前计算,小优化。
  • \(O(nlogn)\) 斜率优化。

洛谷数据很水,\(O(n^2)\) 就可以过,加强版的链接贴上
loj10186

题解

Part 1:暴力算法

\(f[i][j]\) 表示前 \(i\) 个任务分成 \(j\) 组所能取得的最小价值,则我们可以枚举断点 \(k(k \le i)\) ,则 \([1,k-1]\) 分成 \(j-1\) 组,\([k,i]\) 分成单独一组,然后加上价值,转移方程即为:

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

接下来解释转移方程,首先 \(st\) 数组表示时间的前缀和,\(sf\) 数组表示费用系数的前缀和。因为我们前面分成了 \(j\) 组,所以停止的时间就是 \(s\times j\) ,再加上本次的时间,乘上费用系数,就是价值。

贴部分代码

memset(f,127,sizeof f);
f[0][0]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=i;k++)
	f[i][j]=min(f[i][j],f[k-1][j-1]+(s*j+st[i])*(sf[i]-sf[k-1]));

Part 2:小优化

观察原状态转移方程,发现其实我们不需要记录分了多少组,因为当我们多分了一个组时,本组及后续的所有组时间都要加上一个 \(s\),那么设 \(f[i]\) 表示前 \(i\) 个任务的最小价值,枚举从 \(j(j \le i)\) 点转移,状态转移方程为:

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

其中的 \(s*(sf[n]-sf[j-1])\) 就是后续的组的费用,当然,包括本组。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
void read(int &x)
{
	char ch=getchar();
	int r=0,w=1;
	while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
	while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
	x=r*w;
}
const int N=1e4+100;
int st[N],sf[N],n,s,f[N];
main()
{
	read(n);read(s);
	for(int i=1,t,f;i<=n;i++)
	{
		read(t);read(f);
		st[i]=st[i-1]+t;
		sf[i]=sf[i-1]+f;
	}
	memset(f,127,sizeof f);
	f[0]=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=i;j++)
		f[i]=min(f[i],f[j-1]+st[i]*(sf[i]-sf[j-1])+s*(sf[n]-sf[j-1]));
	cout<<f[n];
	return 0;
}
/**/

Part 3 斜率优化(重头戏)

首先,我们对转移方程进行一些更改(前面为了方便理解)

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

说白了就是把 \(j-1\) 换成 \(j\),为了方便后续的斜率优化,其实是等价的。
然后,我们去掉 \(\min\) (还是为了方便):

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

把里面的单项式乘多项式拆开,并把与 \(j\) 无关的移到等号左边:

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

考虑一次函数 \(y=kx+b\),将其变形为 \(b=y-kx\),那么对于上面的转移方程,有:

\[y[j]=f[j]\\ k[i]=st[i]+s\\ x[j]=sf[j]\\ \]

然后,我们就可以把斜率优化的标程粘过来,改掉上面的 \(x,y,k\) 和转移方程,就水过了本题。

Code:

#include<bits/stdc++.h>
#define int long long
#define db double
using namespace std;
void read(int &x)
{
	char ch=getchar();
	int r=0,w=1;
	while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
	while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
	x=r*w;
}
const int N=3e5+100;
int n,s,st[N],sf[N],f[N];
int x[N],y[N],top,stk[N];
db stk_k[N],k[N];
void find(int k,int x)
{
	int i=lower_bound(stk_k+2,stk_k+top+1,k)-stk_k;
	i=stk[i-1];
	f[x]=f[i]-(st[x]+s)*sf[i]+st[x]*sf[x]+s*sf[n];
}
const db inf=1e9;
db getk(int a,int b)
{
	return x[a]==x[b]?inf*(y[b]-y[a]):1.0*(y[b]-y[a])/(x[b]-x[a]);
}
void insert(int x)
{
	while(top>=2&&getk(stk[top-1],stk[top])>getk(stk[top],x))top--;
	stk[++top]=x;
	stk_k[top]=getk(stk[top-1],stk[top]);
}
main()
{
	read(n);read(s);
	for(int i=1,t,f;i<=n;i++)
		read(t),read(f),st[i]=st[i-1]+t,sf[i]=sf[i-1]+f;
	stk[++top]=0;
	for(int i=1;i<=n;i++)
	{
		k[i]=st[i]+s;
		find(k[i],i);
		x[i]=sf[i];
		y[i]=f[i];
		insert(i);
	}
	cout<<f[n];
	return 0;
}
posted @ 2022-07-06 10:56  Epoch_L  阅读(20)  评论(0编辑  收藏  举报