[BZOJ3380]一套NOIP膜你题:小Q的新玩具

来源:互联网 发布:stc单片机开发 编辑:程序博客网 时间:2024/04/29 10:37
【问题描述】

期末考试完了,小Q得到了一件套新玩具,总共有N个零件。现在小Q想把新玩具搬回家里,可是他遭遇了新的问题:每个零件有自己的重量Wi,小Q要出租车把它们带回家。车每次只能运总重量和小于Lim的玩具,按照其中最重的玩具的重量收费。零件不能拆分成更小的部分。为了不打乱零件的顺序,增加自己拼装的难度,每次装车只能装连续的部分。现在想请你帮助小Q计算把玩具全部装回家的费用。


【输入】

第一行两个整数N和Limit。

接下来的N行,每行一个整数,代表第i个零件的的重量。


【输出】

第一行一个数字,表示答案。


【样例输入】
8 17
2
2
2
8
1
8
2

1


【样例输出】

12


【数据范围】
对于30%的数据,N和M<=1000

对于100%的数据,N和M<=300000题解:


题解:

dp[i]:以第i个零件为结尾的最小花费

方程式很好想:dp[i]=min{ dp[j]+max[j+1][i] }。max[l][r]的值就是区间L到R之间的Wi最大值。

可以预处理出来 max,然后O(n^2)的状态转移了,但这样一定超时。需要优化。


考虑决策点。

假如当前正在选择 dp[i]的决策点。对于每个i来说,转移点 j向左移动时,max[j+1][i]单调不递减。所以我们看可以维护一个决策点的队列q,满足区间和不大于Lim且 Wq1>Wq2>…>Wqi 且队列 q 中包含所有满足上述条件的决策点。那么,这里面的决策点一定有最优的决策点。

可能说的不是很明白,不懂的可以看:http://blog.csdn.net/qq_37816449/article/details/76096018


#include<cstdio>#include<set>using namespace std;typedef long long LL;const int N=300005;int n, que[N];LL Lim, w[N], sum[N], dp[N];struct pque {multiset< LL > s;void push( LL v ) { s.insert( v ); }void del( LL v ) { s.erase( s.lower_bound(v) ); }LL top() { return *s.begin(); }}pq;int main() {scanf( "%d%lld", &n, &Lim );for( int i=1; i<=n; i++ )scanf( "%lld", &w[i] ), sum[i]=sum[i-1]+w[i];pq.push( dp[1]=w[1] );int l=1, r=0, left=1; que[ ++r ]=1;    for( int i=2; i<=n; i++ ) {while( sum[i]-sum[left-1]>Lim && left<i ) {pq.del( w[ que[l] ]+dp[left-1] );if( que[l]==left ) l++, left++;else left++, pq.push( w[ que[l] ]+dp[left-1] );}while( l<=r && w[i]>=w[ que[r] ] ) {if ( l==r ) pq.del( w[ que[r] ]+dp[left-1] );else pq.del( w[ que[r] ]+dp[ que[r-1] ] );--r;}que[ ++r ]=i;if( l==r ) pq.push( w[i]+dp[left-1] );else pq.push( w[i]+dp[ que[r-1] ] );dp[i]=pq.top();}printf( "%lld\n", dp[n] );return 0;}


原创粉丝点击