【问题描述】期末考试完了,小Q得到了一件套新玩具,总共有N个零件。现在小Q想把新玩具搬回家里,可是他遭遇了新的问题:每个零件有自己的重量Wi,小Q要出租车把它们带回家。车每次只能运总重量和小于Lim的玩具,按照其中最重的玩具的重量收费。零件不能拆分成更小的部分。为了不打乱零件的顺序,增加自己拼装的难度,每次装车只能装连续的部分。现在想请你帮助小Q计算把玩具全部装回家的费用。
【输入】第一行两个整数N和Limit。
接下来的N行,每行一个整数,代表第i个零件的的重量。
【输出】第一行一个数字,表示答案。
【样例输入】8 1722281821
【样例输出】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; }
[BZOJ3380]一套NOIP膜你题:小Q的新玩具
最新推荐文章于 2020-03-04 09:34:20 发布