原文出处:http://txhwind.blog.163.com/blog/static/2035241792012112285146817/(版权为原作者所有,本博文无营利目的,不构成侵权)

题目大意:(POJ 3017 也是这个)

n本书有各自的高度h_i和宽度w_i. 现在要把它分成若干段,要求每段总宽度不超过l,某段的高度为其中书的高度的最大值。求最小的所有段的高度和。
 
简单题解:
预处理w的前缀和s.
首先得到动规方程:f[i]=min{f[j]+max{h[j+1..i]} | s[i]-s[j]<=l}.
直接做是平方的,如何优化呢?
我们发现f[j]不减,且对某个i,max{h[j+1..i]}不增。
考虑维护hmax值相同的段。那么从i-1到i时,用h[i]把前面hmax比它小的段合并掉。
平衡树维护每段最前面的f值,决策时取最小的。
翱犇思路:用线段树维护所有决策,每次将一段的hmax修改为h[i].
 #include<stdio.h>
#include<set> using std::multiset; const int MAXN=+; int h[MAXN],s[MAXN],w[MAXN];
long long f[MAXN];
int main()
{
freopen("bookshelf.in","r",stdin);
freopen("bookshelf.out","w",stdout);
int n,l,i;
int sum=,head=,tail=;
multiset<long long> val;
scanf("%d%d",&n,&l);
for(i=;i<=n;++i)
{
scanf("%d%d",h+i,w+i); //s记录每段开头
for(s[++tail]=i-;head<tail && h[i]>=h[s[tail]];--tail)
val.erase(val.find(f[s[tail-]]+h[s[tail]]));
val.insert(f[s[tail]]+h[i]);
s[tail+]=i;
for(sum+=w[i];sum>l;sum-=w[s[head]])
{
val.erase(val.find(f[s[head]]+h[s[head+]]));
if(++s[head]==s[head+])
++head;
else val.insert(f[s[head]]+h[s[head+]]);
}
f[i]=*val.begin();
}
printf("%lld\n",f[n]);
return ;
}

以下是我的同学的解题报告:不涉及任何侵权问题。。同学的权,随便侵。。。。

这道题的原型是POJ的3017,只是W[I]换成数列中的权值而已;
对题目进行分析,不难得到动规方程f[i]=min(f[j]+max(h[j+1],h[j+2],h[j+3]....h[i]))其中w[j+1]+w[j+2]+...w[j]<=l;

但是这只是O(n^2)的做法,必须想出优化办法;

首先我们会发现一个i状态会与许多j的子状态相关,这种情况大多都是与单调队列的优化相关;但是这道题并不这么明显;

首先,我们向求max的h[j]值方向思考,不难维护出一个单调递减,队头到i的w[i]之和小于l的最大值单调队列;但是这样就将f的值给忽略掉了;

所以我们需要想出f数组的特点,不难看出这是单调递增的,

所以我们猜想:每一个单调队列中的决策点相对于后面的一段都有可能是最优决策点,但是如何证明呢?

如果这个不是最优决策点,那么他后面的一段区间的最大值都与该点相等,而f非递增,所以决策都没有在单调队列中的决策好;

既然我们发现f与单调队列也有关系,那么此题单调队列的方向就已经确定;

维护上述的一个单调队列,在计算f[i]时在单调队列中的所有点都有可能成为决策点,对于这种情况可以用平衡树进行维护,但代码还有许多细节处理

  #include<cstdio>
#include<set>
#include<iostream>
using namespace std;
const int mm=;
int a[mm],q[mm],w[mm];
long long f[mm],sum,tmp,m;
multiset<long long> sbt;
int i,l,r,p,n;
int main()
{
scanf("%d%lld",&n,&m);
sbt.clear();
sum=l=,f[n]=r=-;
for(p=i=;i<=n;++i)
{
scanf("%d%d",&a[i],&w[i]);
sum+=w[i];
while(sum>m)sum-=w[p++];
while(l<=r&&a[i]>=a[q[r]])
{
if(l<r)sbt.erase(f[q[r-]]+a[q[r]]);
--r;
}
q[++r]=i;
if(l<r)sbt.insert(f[q[r-]]+a[q[r]]); //手动模拟下就会发现对于边界外决策点,其待定决策值为f[q[i]-1]+a[q[i]];
while(q[l]<p)
{
if(l<r)sbt.erase(f[q[l]]+a[q[l+]]);
++l;
}
f[i]=f[p-]+a[q[l]]; //在i为当前最大值时用p数组更新 tmp=*sbt.begin();
if(f[i]>tmp)f[i]=tmp;
}
cout<<f[n];
return ;
}

摘自另一地方。http://www.cnblogs.com/kedebug/archive/2013/03/01/2939428.html

题意:

长度为 n 的数列,要求把这个数列划分为任意块,每块的元素和小于 m,使得所有块的最大值的和最小。

思路:

1. 很明显的一个转移方程是: dp[i] = max(dp[j] + max(a[j+1], a[j+2], ..., a[i])); 其中满足 sum[i] - sum[j] <= M 的 j 都算,这是一个 O(N * N) 的解法。

2. 如果不去优化枚举 j 的这个过程,就会直接 TLE,如何优化?联系到求最大值,可以想到用最大值的单调队列,队列里面存放的是以 i 为结尾,窗口里面的元素和

恰好不大于 M 的 j 为左边界,即恰好有 sum[i] - sum[j-1] <= M.

3. 如果是这样的话,还不能够达到目标,因为 1 中最优值点的 j 的选择可能是窗口中的任意一个值,这时候遇到一个困难:确定 j 的位置,使情况最优。

4. 考虑到 dp[i] 是一个单调增的数组,而窗口中的元素是关于最大值递减的,先让 M 足够大,从简单的角度来考虑:对于 a[1] = 8, a[2] = 5, a[3] = 7,对于 dp[3]

则有 dp[3] = min(dp[0] + 8, dp[1] + 7, dp[2] + 7),显然 dp[1] <= dp[2],则进一步优化有 dp[3] = min(dp[0] + 8, dp[1] + 7);

5. 从 4 中我们可以看出点端倪,单调队列中所指向的元素也恰好是 8, 7,所以得出的结论有:对于 dp[i] 只要选择单调队列中所指向的位置的 j 即可保证结果是最优的。

6. 如何维护单调队列中的 dp[j] + max(a[j+1], a[j+2], ..., a[i]),则考虑使用一个平衡树,这样可以把时间复杂度降到最低。

#include <iostream>
#include <set>
#include <algorithm>
using namespace std; #define LL long long int const int MAXN = 100010; int num[MAXN], deq[MAXN];
LL dp[MAXN];
multiset<LL> optset; int main()
{
LL N, M; scanf("%lld %lld", &N, &M);
for (int i = 1; i <= N; ++i)
scanf("%d", &num[i]); LL sum = 0;
int s = 0, e = -1, p = 1;
bool flag = false; optset.clear();
for (int i = 1; i <= N; ++i)
{
if (num[i] > M)
{
flag = true;
break;
} sum += num[i];
while (sum > M)
sum -= num[p++]; while (s <= e && num[i] >= num[deq[e]])
{
if (s < e)
optset.erase(dp[deq[e-1]] + num[deq[e]]);
--e;
} deq[++e] = i;
if (s < e)
optset.insert(dp[deq[e-1]] + num[deq[e]]); while (deq[s] < p)
{
if (s < e)
optset.erase(dp[deq[s]] + num[deq[s+1]]);
++s;
} dp[i] = dp[p-1] + num[deq[s]];
if (s < e && dp[i] > *optset.begin())
dp[i] = *optset.begin();
}
if (flag)
dp[N] = -1;
printf("%lld\n", dp[N]);
return 0;
}

USACO OPEN 12 BOOKSELF(转)的更多相关文章

  1. usaco 2009 12 过路费

    最近学的图论,oj上的这道题卡了我一上午,写一下总结. 题目描述: 跟所有人一样,农夫约翰以着宁教我负天下牛,休教天下牛负我(原文:宁我负人,休教人负我)的伟大精神,日日夜夜苦思生财之道.为了发财,他 ...

  2. Solution -「USACO 2020.12 P」Spaceship

    \(\mathcal{Description}\)   Link.   Bessie 在一张含 \(n\) 个结点的有向图上遍历,站在某个结点上时,她必须按下自己手中 \(m\) 个按钮中处于激活状态 ...

  3. Solution -「USACO 2020.12 P」Sleeping Cows

    \(\mathcal{Description}\)   Link.   有 \(n\) 个牛棚,大小为 \(t_{1..n}\),\(n\) 头奶牛,大小为 \(s_{1..n}\),奶牛只能住进不小 ...

  4. Poj 3259 Wormholes(spfa判负环)

    Wormholes Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 42366 Accepted: 15560 传送门 Descr ...

  5. RE:ゼロから始める AFO 生活

    新建这篇博客的时候发现自己在NOI之后只发过两三篇博客,而且都基本上没什么实质性内容. 果然是巨大混混人啊. 本文承接上篇(不过好像烂尾了),旨在记录一些有趣(?)的内容. 12.23 北大集训过去好 ...

  6. python 各模块

    01 关于本书 02 代码约定 03 关于例子 04 如何联系我们 1 核心模块 11 介绍 111 内建函数和异常 112 操作系统接口模块 113 类型支持模块 114 正则表达式 115 语言支 ...

  7. Python Standard Library

    Python Standard Library "We'd like to pretend that 'Fredrik' is a role, but even hundreds of vo ...

  8. 在mybatis中写sql语句的一些体会

    本文会使用一个案例,就mybatis的一些基础语法进行讲解.案例中使用到的数据库表和对象如下: article表:这个表存放的是文章的基础信息 -- ------------------------- ...

  9. USACO Section 1.1-2 Greedy Gift Givers

    Greedy Gift Givers 贪婪的送礼者 对于一群(NP个)要互送礼物的朋友,GY要确定每个人送出的钱比收到的多多少. 在这一个问题中,每个人都准备了一些钱来送礼物,而这些钱将会被平均分给那 ...

随机推荐

  1. UICollectionView的简单使用和常用代理方法

    UICollectionView相对于UITableView有更加自由的布局,做出的界面可变性更大最近开始接触使用UICollectionView,整理了一下常用的代理方法 首先需要先添加UIColl ...

  2. (转) ROS NAMING AND NAMESPACES

    原文地址:http://nootrix.com/2013/08/ros-namespaces/   In this tutorial, we will be talking about ROS nam ...

  3. DOM 对象之 document.all

    1.document.all是页面内所有元素的一个集合: 2.经测试在chrome,safari,opera,ie中均返回一个HTMLALLCollection[xx]对象,在FF中返回是一个unde ...

  4. 《javascript高级程序设计》笔记4.1.4:检测类型

    javascript类型检测这节主要讲了typeof和instanceof操作符. 一.typeof操作符: 1.typeof在检测基本数据类型时十分方便,针对4种基本数据类型string.numbe ...

  5. debian修改系统语言为英文

    原文地址:http://www.chenyudong.com/archives/debian-change-locale-language.html 修改/etc/default/locale 文件里 ...

  6. NodeJS和C++的性能比较(转)

    原文地址: http://www.web-tinker.com/article/20374.html 前段时间做了个实验,测试了1E9次的空循环在NodeJS和C++中的执行用时.于是我和小伙伴们瞬间 ...

  7. 用Dockerfile构建docker image

    dockerfile是为快速构建docker image而设计的,当你使用docker build 命令的时候,docker 会读取当前目录下的命名为Dockerfile(首字母大写)的纯文本文件并执 ...

  8. RenderPartial RenderAction Partial Action

    MVC Razor中有不同的展现partial view的方法,许多开发人员子在选择使用 RenderPartial or RenderAction or Partial or Action help ...

  9. DEDECMS织梦全站动态化访问(包括自由列表freelist)及发布内容时自动动态化设置

    DEDECMS织梦 - 全站已有内容全部设置为动态化访问(包括自由列表freelist),以及发布内容时自动为动态化,设置分为三个步骤: 1.将所有文档设置为“仅动态”:执行以下mysql语句:upd ...

  10. ubuntu 安装openproj-1.4-2.noarch.rpm

    一 openproj是rpm包,ubuntu下需要转成deb安装.具体步骤1:下载:http://sourceforge.net/projects/openproj/2:安装alien sudo ap ...