zoukankan      html  css  js  c++  java
  • [USACO 2012 Open Gold] Bookshelf【优化dp】

    传送门1:http://www.usaco.org/index.php?page=viewproblem2&cpid=138

    传送门2:http://www.lydsy.com/JudgeOnline/problem.php?id=2678

    最开始没看到要将那些书按顺序放!!一定是按顺序!我还以为是自己安排那个书架,白费了我好久时间。

    然而看对题之后仍然不会。。。

    令f(i)表示前i本书已经放到书架上,且第i本是某个书架的最后一本的最小高度和,则

    f(i) = min { f(j) + max { h[j + 1], h[j + 2]....., h[i] } }, 其中w[i + 1] + w[i + 2] + ... + w[j] <= L.

    裸的是O(N^2)的(好像Silver版本的这一题就是O(N^2)可以过),所以需要优化。(又是dp优化!每次看dp优化少说都要2个钟头,烦)

    显然,f(j)随j的增大而增大(非严格增大,即大于等于),而对于不变的i,max { h[j + 1], h[j + 2]....., h[i] } 随j的增大而减小(非严格减小,即小于等于),因此我们可以把i前面的所有j,按照max { h[j + 1], h[j + 2]....., h[i] }的大小来分块,把这个值相等的j分到一块去,那么对于这一块j,显然取最前面那个最好,因为根据刚刚所说,f(j)随j的增大而增大(非严格增大,即大于等于)。然而有很多块,那么应该取那一块的最前面那个呢?我们应该用一个平衡树(multiset,就是STL set的可以重复元素版本)把所有可以取的值存起来,并在dp过程中不停地删除那些不能取的值,并添加新的可以取的值。具体见代码。

    #include <cstdio>
    #include <set>
    
    const int maxn = 100005;
    
    int n, ttttttttt[maxn], num, *tail = ttttttttt;
    long long L, s[maxn], h[maxn], f[maxn];
    std::multiset<long long> val;
    
    int main(void) {
    	freopen("bookshelf.in", "r", stdin);
    	freopen("bookshelf.out", "w", stdout);
    	scanf("%d%I64d", &n, &L);
    	for (int i = 1; i <= n; ++i) {
    		scanf("%I64d%I64d", h + i, s + i);
    		s[i] += s[i - 1];
    	}
    	
    	// num表示现在拥有块的个数, tail[i]表示第i个块的结尾位置,
    	// 注意tail是一个指针,这是有方便之处的,之后就会看到 
    	for (int i = 1; i <= n; ++i) {
    		// 下面是将第i本书考虑进去后,重新更新分块 
    		while (num && h[i] >= h[tail[num]]) {
    			val.erase(val.find(f[tail[num - 1]] + h[tail[num]]));
    			--num;
    		}
    		tail[++num] = i;
    		val.insert(f[tail[num - 1]] + h[i]);
    		
    		// tail[0]应该表示的是合法的j使得前缀和s[i] - s[j] <= L
    		// 当 > L时,就应该erase掉对应的值,然后++tail[0],但如果
    		// 下一个就是这个块的结尾了,就要指针移动啦,因为这个块整
    		// 个都被删除了,然后再--num 
    		while (s[i] - s[tail[0]] > L) {
    			val.erase(val.find(f[tail[0]] + h[tail[1]]));
    			if (tail[0] + 1 == tail[1]) {
    				++tail;
    				--num;
    			}
    			else {
    				val.insert(f[tail[0] + 1] + h[tail[1]]);
    				++tail[0];
    			}
    		}
    		
    		f[i] = *val.begin();
    	}
    	printf("%I64d
    ", f[n]);
    	return 0;
    }
    

    官方题解是记录了一个块有多大,我是记录了这个快的结尾位置,都一样。

  • 相关阅读:
    Typora上传图片设置
    SpringBoot入门详细教程
    vue
    我才学习到的严格模式(越往后看,越感觉复杂啊)
    js中三种定义变量的方式const, var, let的区别。
    冒泡
    tab切换
    日历
    jquery实现拖拽进度条并显示百分比的特效
    抽奖代码
  • 原文地址:https://www.cnblogs.com/ciao-sora/p/6054559.html
Copyright © 2011-2022 走看看