[HNOI2008]玩具装箱TOY

problem



Solution

20’DP:

d p [ i ] = m i n ( d p [ j ] + ( s u m [ i ] − s u m [ j − 1 ] + i − j − L ) 2 ) dp[i] = min(dp[j] + (sum[i] - sum[j - 1] + i - j - L)^2) dp[i]=min(dp[j]+(sum[i]sum[j1]+ijL)2)

code:

#include<bits/stdc++.h>
#define N 500005
#define int long long
using namespace std;
int sqr(int x){
	return x * x;
}
int n, L, a[N], sum[N], f[N];
signed main(){
	scanf("%lld%lld", &n, &L);
	for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]), sum[i] = a[i] + sum[i - 1];
	memset(f, 0x3f, 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] + sqr(sum[i] - sum[j - 1] + i - j - L));
		}
	}
	printf("%lld", f[n]);
	return 0;
}

斜率优化

可以变成
d p [ i ] = m i n ( d p [ j ] + ( s u m [ i ] − s u m [ j ] + i − j − L − 1 ) 2 ) dp[i] = min(dp[j] + (sum[i] - sum[j] + i - j - L - 1)^2) dp[i]=min(dp[j]+(sum[i]sum[j]+ijL1)2)

a [ i ] = s u m [ i ] + i , b [ i ] = s u m [ i ] + i + L + 1 a[i] = sum[i] + i, b[i]=sum[i]+i+L+1 a[i]=sum[i]+i,b[i]=sum[i]+i+L+1

d p [ i ] = m i n ( d p [ j ] + ( a [ i ] − b [ j ] ) 2 ) dp[i] = min(dp[j] + (a[i]-b[j])^2) dp[i]=min(dp[j]+(a[i]b[j])2)

d p [ i ] = d p [ j ] + a [ i ] 2 + b [ j ] 2 − 2 ⋅ a [ i ] ⋅ b [ j ] dp[i] = dp[j] + a[i]^2+b[j]^2-2·a[i]·b[j] dp[i]=dp[j]+a[i]2+b[j]22a[i]b[j]

2 ⋅ a [ i ] ⋅ b [ j ] + d p [ i ] − a [ i ] 2 = d p [ j ] + b [ j ] 2 2·a[i]·b[j] + dp[i] -a[i]^2= dp[j]+b[j]^2 2a[i]b[j]+dp[i]a[i]2=dp[j]+b[j]2

b [ j ] b[j] b[j]看做 x x x d p [ j ] + b [ j ] 2 dp[j]+b[j]^2 dp[j]+b[j]2看做 y y y

y = 2 ⋅ a [ i ] x + d p [ i ] − a [ i ] 2 y=2·a[i]x+dp[i]-a[i]^2 y=2a[i]x+dp[i]a[i]2

这就是一条直线的解析式, y = k x + b , k = 2 ⋅ a [ i ] , b = d p [ i ] − a [ i ] 2 y=kx+b, k=2·a[i],b=dp[i]-a[i]^2 y=kx+b,k=2a[i],b=dp[i]a[i]2

要找一个点 P ( b [ j ] , d p [ j ] + b [ j ] 2 ) P(b[j],dp[j]+b[j]^2) P(b[j],dp[j]+b[j]2)使得上面的斜率为2*a[i]的直线过这个点且与 y y y轴截距 ( b = d p [ i ] − a [ i ] 2 ) (b=dp[i]-a[i]^2) (b=dp[i]a[i]2)最小

因为斜率 k = 2 ⋅ a [ i ] k=2·a[i] k=2a[i]是固定的,所以要求的就是最小的 b b b,把它加上 a [ i ] 2 a[i]^2 a[i]2就是 d p [ i ] dp[i] dp[i]的值


大概就是这样

233

就变成了用一条斜率为 k = 2 ⋅ a [ i ] k=2·a[i] k=2a[i]的直线从下到上(或从右到左)扫过去,碰到的第一个点后把直线和 y y y轴的截距求出来加上 a [ i ] a[i] a[i]就是答案

很明显就是维护一个下凸壳(1/4)


然后就单调队列维护嘛。。

S l o p e ( i , j ) Slope(i,j) Slope(i,j) P i , P j P_i, P_j Pi,Pj 的斜率

如果 S l o p e ( Q h e a d , Q h e a d + 1 ) &lt; = 2 ⋅ a [ i ] Slope(Q_{head}, Q_{head + 1}) &lt;=2·a[i] Slope(Qhead,Qhead+1)<=2a[i] h e a d + + head ++ head++

S l o p e ( Q t a i l , Q t a i l − 1 ) &gt; = S l o p e ( Q t a i l − 1 , i ) Slope(Q_{tail}, Q_{tail - 1}) &gt;= Slope(Q_{tail - 1}, i) Slope(Qtail,Qtail1)>=Slope(Qtail1,i) t a i l − − tail -- tail

code:

#include<bits/stdc++.h>
#define N 1000005
#define int long long//hei hei
#define double long double //开long double 防止精度丢失
using namespace std;
int q[N], n, L, sum[N], dp[N];
double a(int i) {return sum[i] + i;}
double b(int i) {return sum[i] + i + L + 1;}
double x(int i) {return b(i);}
double y(int i) {return dp[i] + b(i) * b(i);}
double Slope(int i, int j) {return (y(i) - y(j)) / (x(i) - x(j));}
signed main(){
	scanf("%lld%lld", &n, &L);
	for(int i = 1; i <= n; i ++) scanf("%lld", &sum[i]), sum[i] += sum[i - 1];
	int l = 1, r = 1;
	for(int i = 1; i <= n; i ++){
		while(l < r && Slope(q[l], q[l + 1]) < 2 * a(i)) l ++;
		int j = q[l]; 
		dp[i] = dp[j] + (a(i) - b(j)) * (a(i) - b(j));//截距就是把x=2*b[j]代入斜率为2*a[i]的直线,解除b,加上a[i]就是dp[i],化简完就是这样, 或者直接转移也可以
		while(l < r && Slope(q[r - 1], q[r]) > Slope(i, q[r - 1])) r --;
		q[++ r] = i;
	}
	printf("%lld", dp[n]);
	return 0;
}

posted @ 2019-06-10 12:47  lahlah  阅读(25)  评论(0编辑  收藏  举报