[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过程中不停地删除那些不能取的值,并添加新的可以取的值。具体见代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #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\n" , f[n]); return 0; } |
官方题解是记录了一个块有多大,我是记录了这个快的结尾位置,都一样。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?