poj 3017

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

思路:

很明显的一个转移方程是: dp[i] = max(dp[j] + max(a[j+1], a[j+2], ..., a[i])); 其中满足 sum[i] - sum[j] <= M 的 j 都算,这是一个 O(N * N) 的解法,肯定超时。,那么就要用优化的方法 ,对着题解看了一个小时终于看懂了。

f[j]表示前j个数分为若干份,最优解。

g[i]表示使f[i]取得最优解的j,即最后一份为从g[i]分到i。

把第i+1个数加入进来。

如果超过了m,则把前面的数减去,直至小于m。

当总数小于m时:

如果a[i+1]小于mx(g[i]到i中的最大值)则可以忽略,f[i]=f[i-1],g[i]=g[i-1];

如果a[i+1]大于mx,则要确定最优值。(优化)因为f[i+1]的最优值不会再(g[i-1],i)内,所以可以直接从p到i+1计算。p为第p个数使p到i+1的总和小于m。但如果g[i-1]小于p,就要从i计算到p。

(g[i-1]>=p?g[i-1]:i);意思是如果之前的最大值不在当前字块内,那么就从i->p计算最大值之和的最小值;如果最大值还在的话,那么就只需要在计算g【i-1】== p处的最大值之和的最小值。其实就是更新一下f【i】和g【i】;如果f[i]>f[j-1]+mx的话,那么最优解肯定不是以g[i]为下标的那个解。那么更新f【i】=j;如果是f[i]<f[j-1]+mx,那么就不更新g[i]。

#include<iostream>
#include <cstdio>
using namespace std;
#define maxn 101010
typedef long long ll;
const ll inf=1000000000;
ll f[maxn];
int g[maxn];
ll a[maxn];
int n;
ll m;
int main()
{
    int i,j;
    while(scanf("%d%I64d",&n,&m)!=-1)
    {
        for(i=1; i<=n; i++)
        {
            scanf("%I64d",&a[i]);
            if(a[i]>m)
            {
                printf("-1\n");
                return 0;
            }
        }
        f[1]=a[1];
        g[1]=1;
        int p=1;
        f[0]=0;//因为当第一次更新的时候那个需要计算f[0]+mx,所以我们应该给f【0】赋值
        ll mx=a[1],sum=a[1];

        for(i=2; i<=n; i++)
        {
            int k=0;
            f[i]=inf;
            sum+=a[i];
            while(sum>m)sum-=a[p++];
            if(mx>=a[i]&&g[i-1]>=p)
            {
                f[i]=f[i-1];
                g[i]=g[i-1];
            }
            else
            {
                mx=a[i];
                 k=(g[i-1]>=p?g[i-1]:i);

                for(j=k; j>=p; j--)
                {
                    mx=max(mx,a[j]);
                    g[i]=f[i]>mx+f[j-1]?j:g[i];
                    f[i]=min(f[i],f[j-1]+mx);
                }
                mx=f[i]-f[g[i]-1];
            }

        }
        printf("%I64d\n",f[n]);
    }
}

时间: 2024-07-30 13:44:17

poj 3017的相关文章

poj 3017 Cut the Sequence

poj 3017 Cut the Sequence 单调队列 题意是:把一个 长度 为 n 的 数列 分成任意份,使每一份的和不超过m,求每一份的最大值的和,使和最小 动态规划方程 是 f [i] = f[j] + max{ f[j+1] , f[j+2] , f[i] }; 朴素的算法是 O(n^2); 用 单调队列 表示一个递减 的 队列 ,则 不需要 求 每块区域的最大值 哎哎哎……不知道怎么说 #include <iostream> #include <cstdio> #i

POJ 3017 单调队列dp

Cut the Sequence Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8764   Accepted: 2576 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 subseque

POJ 3017 Cut the Sequence (单调队列优化DP)

POJ 3017 Cut the Sequence (单调队列优化DP) ACM 题目地址: POJ 3017 Cut the Sequence 题意: 将一个由N个数组成的序列划分成若干段,要求每段数字的和不超过M,求[每段的最大值]的和 的最小的划分方法,输出这个最小的和. 分析: 方程是:dp[i] = min(dp[j]+maxsum[j+1][i]) 但复杂度n*n太高,需要优化. 可以用单调队列+BST优化,其实只需要维护每一段的最大值. 看了这篇题解的:http://blog.cs

poj 3017 Cut the Sequence(单调队列优化 )

题目链接:http://poj.org/problem?id=3017 题意:给你一个长度为n的数列,要求把这个数列划分为任意块,每块的元素和小于m,使得所有块的最大值的和最小 分析:这题很快就能想到一个DP方程 f[ i ]=min{ f[ j ] +max{ a[ k ] }}( b[ i ]<j<i,j<k<=i)     b[ i ]到 i的和大于m 这个方程的复杂度是O(n^2),明显要超时的(怎么discuss都说数据弱呢= =) 然后是优化了,首先当然是要优化一个最大

poj 3017 单调队列

只能说是代码美如画.(当然我是指内在逻辑不是我的代码风格,队长看到要理解哈,啊哈哈哈哈哈哈哈哈) 正常思路咯,f[i]=f[j]+max(a[j+1],a[j+2]....a[i]),枚举j,显然硬来会超时,所以需要有一个单调队列来时时把最大值尽快弄出来并且需要一些剪枝: 剪枝条件有两个,一个是和不能超过m,一个是显然f[i]是个非严格递增序列,根据这两个条件剪枝: 则建立单调队列,每当插入新的i时将前面先把和小于等于m的条件做好,然后对于j<i,如果a[j]<a[i]就可以把j丢弃,那么显然

POJ题目分类推荐 (很好很有层次感)

著名题单,最初来源不详.直接来源:http://blog.csdn.net/a1dark/article/details/11714009 OJ上的一些水题(可用来练手和增加自信) (POJ 3299,POJ 2159,POJ 2739,POJ 1083,POJ 2262,POJ 1503,POJ 3006,POJ 2255,POJ 3094) 初期: 一.基本算法: 枚举. (POJ 1753,POJ 2965) 贪心(POJ 1328,POJ 2109,POJ 2586) 递归和分治法. 递

POJ 刷题指南

OJ上的一些水题(可用来练手和增加自信) (POJ 3299,POJ 2159,POJ 2739,POJ 1083,POJ 2262,POJ 1503,POJ 3006,POJ 2255,POJ 3094) 初期: 一.基本算法: 枚举. (POJ 1753,POJ 2965) 贪心(POJ 1328,POJ 2109,POJ 2586) 递归和分治法. 递推. 构造法.(POJ 3295) 模拟法.(POJ 1068,POJ 2632,POJ 1573,POJ 2993,POJ 2996) 二

USACO OPEN 12 BOOKSELF(转)

原文出处: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[

poj 动态规划题目列表及总结

此文转载别人,希望自己能够做完这些题目! 1.POJ动态规划题目列表 容易:1018, 1050, 1083, 1088, 1125, 1143, 1157, 1163, 1178, 1179, 1189, 1208, 1276,1322, 1414, 1456, 1458, 1609, 1644, 1664, 1690, 1699, 1740(博弈),1742, 1887, 1926(马尔科夫矩阵,求平衡), 1936, 1952, 1953, 1958, 1959, 1962, 1975,