• 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
    ", f[n]);
    }
    int main()
    {
    	work();
    	return 0;
    }
    

      

  • 相关阅读:
    写爬虫,怎么可以不会正则呢?
    从 Scrapy 学习模块导入技巧
    博客已搬家至CSDN
    更改JDK默认编码,解决DBeaver乱码问题
    【2020面试】- Java中常见集合的默认大小以及扩容机制
    【2020面试】- CAS机制与自旋锁
    【2020面试】- filter和interceptor的区别
    【20k中级开发】-面试题201117
    【开发笔记】
    RPC 调用和 HTTP 调用的区别
  • 原文地址:https://www.cnblogs.com/lalawu/p/3561079.html
Copyright © 2020-2023  润新知