p2365 & p5785 任务安排

两道题的转移式是一样的,只是优化不同。

\(dp(i)\) 为完成前 \(i\) 个任务的最小花费。

你会发现每次操作后总时间会增加 \(s\) ,这样就不好处理当前时间。

所以可以用前几周讲到的方法,将 \(s\) 的贡献提前计算,那么有转移

\[dp(i)=\min\{dp(j)+(Sumc_i-Sumc_j)(Sumt_i+s)+s(Sumc_n-Sumc_i)\} ~~~ (0 \le j <i) \]

\[dp(i)=\min\{dp(j)+(Sumc_i-Sumc_j)Sumt_i+s(Sumc_n-Sumc_j)\} ~~~ (0 \le j <i) \]

将与 \(j\) 无关的提到外面来

\[dp(i)=\min\{dp(j)-Sumc_jSumt_i-sSumc_j\}+Sumc_iSumt_i+sSumc_n ~~~ (0 \le j <i) \]

我们只需要求出 \(dp(j)-Sumc_jSumt_i-sSumc_j\) 的最小值即可。

P2365

假设有两个决策点 \(j<k\) ,且 \(j\)\(k\) 优。

那么有:

\[dp(j)-Sumc_jSumt_i-sSumc_j<dp(k)-Sumc_kSumt_i-sSumc_k \]

\[(dp(j)-sSumc_j)-(dp(k)-sSumc_k)<Sumt_i(Sumc_j-Sumc_k) \]

\[\frac{(dp(j)-sSumc_j)-(dp(k)-sSumc_k)}{(Sumc_j-Sumc_k)}>Sumt_i \]

这里变号是因为 \(Sumc_j-Sumc_k<0\)

然后就可以斜率优化了。

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

const int MAXN = 3e5;
int n , s , T[ MAXN + 5 ] , C[ MAXN + 5 ];
int Que[ MAXN + 5 ] , Head , Tail;
LL dp[ MAXN + 5 ];

LL fx( int i ) { return C[ i ]; }
LL fy( int i ) { return dp[ i ] - s * C[ i ]; }
double Slope( int i , int j ) {
	return ( fy( i ) - fy( j ) ) * 1.0 / ( fx( i ) - fx( j ) );
}

int main( ) {
	scanf("%d %d",&n,&s);
	for( int i = 1 ; i <= n ; i ++ ) {
		scanf("%d %d",&T[ i ],&C[ i ]);
		T[ i ] += T[ i - 1 ] , C[ i ] += C[ i - 1 ];
	}
	
	Head = 1 , Tail = 0; Que[ ++ Tail ] = 0;
	for( int i = 1 ; i <= n ; i ++ ) {
		for( ; Head + 1 <= Tail && Slope( Que[ Head ] , Que[ Head + 1 ] ) <= T[ i ] ; Head ++ );
		dp[ i ] = dp[ Que[ Head ] ] + 1ll * ( C[ i ] - C[ Que[ Head ] ] ) * T[ i ] + s * ( C[ n ] - C[ Que[ Head ] ] );
		for( ; Head + 1 <= Tail && Slope( Que[ Tail - 1 ] , Que[ Tail ] ) > Slope( Que[ Tail ] , i ) ; Tail -- );
		Que[ ++ Tail ] = i;	
	}
	printf("%lld\n", dp[ n ] );
	return 0;
}

P5785

这道题看起来和上一道一样,但是 \(t\) 可以为负数。

这就意味着 \(Sumt\) 不一定递增,不能直接删掉斜率不大于 \(Sumt\) 的点。

但是仍然可以维护一个斜率递增的队列,在里面二分就可以了。

这道题卡精度,不要用除法。

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

const int MAXN = 3e5;
int n , s , T[ MAXN + 5 ] , C[ MAXN + 5 ];
int Que[ MAXN + 5 ] , Head , Tail;
LL dp[ MAXN + 5 ];

LL fx( int i ) { return C[ i ]; }
LL fy( int i ) { return dp[ i ] - 1ll * s * C[ i ]; }

signed main( ) {
	scanf("%d %d",&n,&s);
	for( int i = 1 ; i <= n ; i ++ ) {
		scanf("%d %d",&T[ i ],&C[ i ]);
		T[ i ] += T[ i - 1 ] , C[ i ] += C[ i - 1 ];
	}
	
	Head = 1 , Tail = 0; Que[ ++ Tail ] = 0;
	for( int i = 1 , opt = 0 ; i <= n ; i ++ ) {
		for( int l = Head , r = Tail ; l <= r ;  ) {
			int Mid = ( l + r ) >> 1;
			if( Mid != Tail && ( fy( Que[ Mid ] ) - fy( Que[ Mid + 1 ] ) ) >= T[ i ] * ( fx( Que[ Mid ] ) - fx( Que[ Mid + 1 ] ) ) ) l = Mid + 1;
			else r = Mid - 1 , opt = Que[ Mid ];
		}
		dp[ i ] = dp[ opt ] + 1ll * ( C[ i ] - C[ opt ] ) * T[ i ] + 1ll * s * ( C[ n ] - C[ opt ] );
		for( ; Head + 1 <= Tail && ( fy( Que[ Tail - 1 ] ) - fy( Que[ Tail ] ) ) * ( fx( Que[ Tail ] ) - fx( i ) ) >= ( ( fy( Que[ Tail ] ) - fy( i ) )  ) * ( fx( Que[ Tail - 1 ] ) - fx( Que[ Tail ] ) ) ; Tail -- );
		Que[ ++ Tail ] = i;
	}
	printf("%lld\n", dp[ n ] );
	return 0;
}
posted @ 2021-04-08 20:30  chihik  阅读(39)  评论(0编辑  收藏  举报