高考集训Day2

D. 花瓶

 

题目描述

qjd 家里有 n 个花瓶,它们排成了一排。
有的花瓶很好看,而有的花瓶不好看,于是 qjd 给每个花瓶都定义了它的美丽度 ai 。但是 qjd 不舍得把不好看的花瓶扔了,也不想变化它们的位置,于是他就打算把这些花瓶分成若干组,每组都是一个连续段。
假设 qjd 把这些花瓶分成了 k 个连续段,记 Si 表示第 i 个连续段中所有花瓶的美丽度之和,他希望最大化:S1S2 + S2S3 + …… + Sk-1Sk。
特别的,k=1  时上述结果为 0。

输入格式

第一行一个正整数 n 。
接下来一行共 n 个整数  ,注意这里 ai 可能是负数。

输出格式

共一行一个整数表示答案。

样例

样例输入

6
2 -1 4 3 -1 0

样例输出

13
CODE
暴力版本的dp
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2003;
const int mod = 998244353;
const int INF = 0x7fffffff;

int n, a[maxn];
ll sum[maxn], f[maxn][maxn], ans;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    freopen("d.in", "r", stdin);
    freopen("d.out", "w", stdout);

    n = read();
    for(int i=1; i<=n; i++)
    {
        a[i] = read();
        sum[i] = sum[i-1] + a[i];
        //printf("sum[%d] == %lld\n", i, sum[i]);
    }
    for(int i=0; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            f[i][j] = -INF;
        }
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<i; j++)
        {
            for(int k=0; k<j; k++)
            {
                //printf("k == %d j == %d i == %d\n", k, j, i);
                f[i][j] = max(f[i][j], f[j][k]+(sum[i]-sum[j])*(sum[j]-sum[k]));
                //printf("cmp: %lld\n", f[j][k]+(sum[i]-sum[j])*(sum[j]-sum[k]));
                //printf("zucheng: f[%d][%d] == %lld ji == %lld\n", j, k, f[j][k], (sum[i]-sum[j])*(sum[j]-sum[k]));
                //printf("f[%d][%d] == %lld\n", i, j, f[i][j]);
            }
        }
    }
    for(int i=1; i<n; i++)
    {
        ans = max(ans, f[n][i]);
        //printf("f[%d][%d] == %lld\n", n, i, f[n][i]);
    }
    printf("%lld", ans);

    return 0;
}
View Code

正解版本

不过我现在还是不知道为什么不能 l<=r 作为删除条件

//我想知道l<=r的结果
//结果证明是0分。
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 5003;
const int mod = 998244353;
const int INF = 0x7fffffff;

int n, a[maxn], id[maxn], l, r, que[maxn];
ll sum[maxn], dp[maxn][maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

bool cmp(int a, int b)
{
    return sum[a] < sum[b];
}

int main()
{
    freopen("d.in", "r", stdin);
    freopen("d.out", "w", stdout);

    n = read();
    for(int i=1; i<=n; i++)
    {
        a[i] = read(); sum[i] = sum[i-1] + a[i]; id[i] = i;
    }
    //k=0可以作为决策,下面遍历要从0开始所以排序也要从0开始
    sort(id, id+1+n, cmp);//按照sum从小到大的顺序排列枚举的编号
    memset(dp, 0xaf, sizeof(dp));
    for(int i=0; i<=n; i++)
    {
        dp[i][0] = 0;
    }
    for(int i=1; i<=n; i++)
    {
        l = 1, r = 0;
        //qes:为什么删队尾和删队首的操作分别要从前后循环呢?
        //i是定值,j是状态变量,k是决策变量,用斜率优化决策变量
        //而我昨天改了一下午又循环了一遍k,充分说明我根本没理解斜率优化在干嘛
        for(int j=0; j<=n; j++)
        {
            if(id[j] < i)//这时sum单调递增
            {
                //这种形式是把除法求斜率乘过去了
                //如果新数和que[r-1]的连线把r围在了下面,那么r永远不会成为最优解
                //一般格式是l<=r,这里特殊注意不能相等是因为?
                //sum[k]越小越好?干嘛不全删光
                while(l<r && (dp[i][que[r]]-dp[i][que[r-1]])*(sum[id[j]]-sum[que[r-1]])
                      <=(dp[i][id[j]]-dp[i][que[r-1]])*(sum[que[r]]-sum[que[r-1]])) r--;
                que[++r] = id[j];
            }
        }
        for(int j=n; j>=0; j--)
        {
            if(id[j] > i)//这时sum单调递减
            {
                //新数的斜率,也就是从上到下平移的那条线的斜率比l和l+1小,那么l就出局了,因为l+1会先和那条线相遇
                while(l<r && (sum[id[j]]-sum[i])*(sum[que[l+1]]-sum[que[l]])
                      <=(dp[i][que[l+1]]-dp[i][que[l]])) l++;
                //操作结束,用队首更新最优解
                dp[id[j]][i] = max(dp[id[j]][i], dp[i][que[l]]+(sum[id[j]]-sum[i])*(sum[i]-sum[que[l]]));
            }
        }
    }
    ll maxx = 0;
    for(int i=0; i<n; i++)
    {
        maxx = max(maxx, dp[n][i]);
    }
    printf("%lld", maxx);

    return 0;
}

 

 
 
posted @ 2022-06-07 20:42  Catherine_leah  阅读(17)  评论(0编辑  收藏  举报
/* */