Cut the Sequence
Time Limit: 2000MS | Memory Limit: 131072K | |
Total Submissions: 7972 | Accepted: 2287 |
Description
Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subsequence of the original sequence. Every part must satisfy that the sum of the integers in the part is not greater than a given integer M. You are to find a cutting that minimizes the sum of the maximum integer of each part.
Input
The first line of input contains two integer N (0 < N ≤ 100 000), M. The following line contains N integers describes the integer sequence. Every integer in the sequence is between 0 and 1 000 000 inclusively.
Output
Output one integer which is the minimum sum of the maximum integer of each part. If no such cuttings exist, output −1.
Sample Input
8 17 2 2 2 8 1 8 2 1
Sample Output
12
Hint
Use 64-bit integer type to hold M.
Source
POJ Monthly–2006.09.29, zhucheng 这题是很好的题目。 思考了很久一直没有想通。 后来突然理解了 可以参照大牛的解释:
http://www.cnblogs.com/staginner/archive/2012/04/02/2429850.html 这讲得比较详细了。
/* * 用dp[i]表示前i项进行划分得到的结果 * dp[i]=min{dp[j],max[j+1,i]}; * 首先假如不考虑m的限制。 * 那么最优决策的那个j点,一定满足a[j] > max(a[j+1~i])就是j点的值要大于j+1到j点的值。 * 很明显的结论,因为假如不成立,那么可以把j点也划分到后面去,得到更优的 * 所以用单调队列维护一个递减数列 a1 a2 a3 a4 ... a[i] ;a1>a2>a3>a4>a[i]; * 这样的话最优决策点只能出现在这些点中。 * 现在加了m的限制。假如上面那个递减数列只有后面一部分是满足m限制的 at,at+1,...a[i]. * 那么决策点就是上面这些点再加上满足m条件的那个边界。 * 所以可以用平衡二叉树维护 * 二叉树里面的值只要随着队列进行维护就好了。 * 可以用multiset实现。 */ #include <iostream> #include <string.h> #include <algorithm> #include <stdio.h> #include <set> using namespace std; const int MAXN=100010; int a[MAXN]; long long sum[MAXN]; int n; long long m; int q[MAXN]; int d[MAXN]; multiset<long long>st; multiset<long long>::iterator it; long long dp[MAXN]; void solve() { int rear,front; rear=front=0; int decision=0;//限制条件 st.clear(); for(int i=1;i<=n;i++) { while(sum[i]-sum[decision]>m)decision++; while(front<rear && q[front]<=decision)//让不符合条件的退出队列 { it=st.find(dp[d[front]]+a[q[front]]); st.erase(it); front++; } while(front<rear && a[i]>=a[q[rear-1]])//维护单调队列 { it=st.find(dp[d[rear-1]]+a[q[rear-1]]); st.erase(it); rear--; } q[rear]=i; if(front<rear)d[rear]=q[rear-1]; else d[rear]=decision; st.insert(dp[d[rear]]+a[i]); rear++; if(d[front]<decision)//更新那个限制点 { it=st.find(dp[d[front]]+a[q[front]]); st.erase(it); d[front]=decision; st.insert(dp[decision]+a[q[front]]); } it=st.begin();//最小值 dp[i]=(*it); } printf("%I64d\n",dp[n]); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt","w",stdout); while(scanf("%d%I64d",&n,&m)==2) { bool flag=true; sum[0]=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; if(a[i]>m)flag=false; } if(!flag) { printf("-1\n"); continue; } solve(); } return 0; }