Cut the Sequence
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 10240 Accepted: 3093
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
【分析】
给定长度 为N的序列,把它分成若干段每和不超过 M,问 分成的所有段里最大值和小是多少?
• f[ i]=min(f[j]+max(a[ j+1]…]=min(f[j]+max(a[ j+1]…]=min(f[j]+max(a[ j+1]…]=min(f[j]+max(a[ j+1]…]=min(f[j]+max(a[ j+1]…i])) ,其中 ∑(a[ j+1]…(a[ j+1]…(a[ j+1]…i])<= M,O(n^2) O(n^2) 。
• 引理:上述方程中决策 j是必要的,当 且仅a[j] 是j到i这一段里的最大值。
• 证明:反证法。记 Max[ j,i ]为j到i的最大值 ,若 Max[ j,i ]=Max[j+1,i ]=Max[j+1,i ]=Max[j+1,i],由f数组的单调性 f[j -1]<=f[j],得到 f[j -1]+Max[ j,i ]<= f[j]+Max[j+1,i] ,这就 说明决策 j一定不如决策 j-1更优 ,与 决策 j是必要的 矛盾,证毕。
• 可以 维护一个 a[ i]值单调递减的 队列 q,该队列 中的 决策都是 必要的 。易 知f[q[k-1]]+a[q[k]](L < k<=R )就是可以对 f[i]进行更新的决策值。
q并不 满足决策值单调,所以要遍历队列取最优解,同时还要手动特殊维护一个队首的决策指针。
题上说要用I64,其实long long也可以。
【代码】
//poj 3017 Cut the Sequence
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=100005;
ll dp[mxn],sum[mxn],q[mxn],a[mxn],g[mxn];
ll m;
int n;
int main()
{
bool flag=0;
int i,j,k;
scanf("%d",&n);
scanf("%I64d",&m);
fo(i,1,n)
{
scanf("%I64d",&a[i]);
if(a[i]>m) flag=1;
sum[i]=sum[i-1]+a[i];
}
if(flag) {printf("-1\n");return 0;}
memset(dp,0x7f,sizeof dp);
dp[0]=0;
int h=1,t=0,shit=1;
fo(i,1,n)
{
while(sum[i]-sum[shit-1]>m) shit++;
while(h<=t && a[q[t]]<=a[i]) t--;
q[++t]=i;
while(h<=t && sum[i]-sum[q[h]-1]>m) h++;
dp[i]=min(dp[i],dp[shit-1]+a[q[h]]);
k=shit-1;
fo(j,h,t)
{
ll tmp=dp[k]+a[q[j]];
dp[i]=min(dp[i],tmp);
k=q[j];
}
}
printf("%I64d\n",dp[n]);
return 0;
}