BZOJ1010玩具装箱[DP+单调性优化]
题目意思我就不多说了。
基本的DP很容易想到的。
设f[i]为前i个玩具放到箱子里面的最小代价。
int a = sum[i] - sum[j] + i - j - 1 - L;
那么f[i] = min(f[j] + a * a);
但是这样去转移的话需要n^2的复杂度。。这样的话就会TLE。
这里的话我们就可以应用到单调性优化或者斜率优化。
先了解一下单调性优化可以用在什么地方。
我们可以证明出对于任意的一个i,他的决策点是j,为了保持最优解,那么在i从1到n的过程中,每一个状态的决策点j都是不下降的。
怎么证明这里就不说了。
然后说一下具体的单调优化的方法
先讲一下几个数组的意思,sta[i]代表第i个状态(也就是f[i])转移的决策点的编号(编号就是第几大)
l[sta[i]]代表这个决策点编号决策的状态的区间左边节点
for(int i = 1 ; i <= n ; ++i)首先枚举i
然后利用二分查找
int find(int x)
{
int left = 1 , right = r , mid = (left + right) / 2;
while(left <= right)
{
if(l[sta[mid]] == x) return mid;
if(l[sta[mid]] < x) left = mid + 1;
else right = mid - 1;
mid = (left + right) / 2;
}
return right;
}
我也不知道怎么描述这个呀。。。大概就是查找i在哪个决策点的区间里。
然后根据编号转移。
接着需要动态的更新sta中其他点的决策点
找到大概在哪个区间,然后对这个区间再二分找具体的某个点
然后更新就ok了
#include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <iostream> #define mod 9999973 #define N 50005 #define ll long long using namespace std; int n , L , c[N] , l[N] , sta[N] , left , right , r; ll sum[N] , f[N]; ll turn(int j , int i) { ll x = i - j + sum[i] - sum[j] - 1; return f[j] + (x - L) * (x - L); } int find(int x) { int left = 1 , right = r , mid = (left + right) / 2; while(left <= right) { if(l[sta[mid]] == x) return mid; if(l[sta[mid]] < x) left = mid + 1; else right = mid - 1; mid = (left + right) / 2; } return right; } void work() { scanf("%d%d", &n, &L); for(int i = 1 ; i <= n ; ++i) scanf("%d", &c[i]) , sum[i] = sum[i - 1] + c[i]; for(int i = 1 ; i <= n ; ++i) l[i] = n + 1; memset(sta , 255 , sizeof(sta)); l[0] = 1 , sta[1] = 0 , r = 1; for(int i = 1 ; i <= n ; ++i) { f[i] = turn(sta[find(i)] , i); while(r && l[sta[r]] > i && turn(i , l[sta[r]]) < turn(sta[r] , l[sta[r]])) --r; if(!r) { sta[++r] = i; l[i] = i; continue; } int left = l[sta[r]] , right = n , mid = (left + right) / 2; while(left <= right) { if(turn(i , mid) > turn(sta[r] , mid)) left = mid + 1; else right = mid - 1; mid = (left + right) / 2; } if(turn(i , left) > turn(sta[r] , left)) continue; sta[++r] = i , l[i] = left; } printf("%lld\n", f[n]); } int main() { work(); return 0; }