Educational Codeforces Round 63D. Beautiful Array(动态规划)

题意:

在一个数组里面,找一个连续子数组使得子数组和最大,这是最大子段和。

现在给定一个数x,要求,求数组中的某一个连续子数组乘上x之后,该数组的最大字段和是多少。

解法:

第一眼:毫无疑问这是线性DP,而且肯定就是最大字段和拓展一下。

第二眼:如果x是正的话,那就找出最大子段和然后乘上x,如果x是负的话,那就找出最小子段和,并且记录这一个最小子段和的位置,然后给对应位置乘上x后,在对原数组进行最大子段和。连wa。。。恍然醒悟,这种贪心的做法似乎不适用,如果x大于0的话显然是没问题的,不过如果x小于0,将最小子段和乘x并不能保证可以得到最优解。

例如:3  -1

-11  10 -11  ,明显只需要变一个11就行了。

第三眼:既然贪心不可行,尝试动态规划,声明dp[maxx][2],dp[i][0]:表示到i为止没有乘m的最大值,dp[i][1]:表示到i为止已经乘了m的最大值,那么转移就可以是:

dp[i][0] = max(dp[i-1][0]+a[i],a[i]);
dp[i][1] = max(max(dp[i-1][0]*m+a[i],dp[i-1][0]+a[i]*m),max(dp[i-1][1]+a[i],a[i]*m));

然而这种状态好像有问题,因为他只考虑了一个前面一部分元素乘m也就是dp[i-1][0]*m或者是当前元素乘m,但是并没有考虑从当前开始后x位乘m。也就是说,他一直考虑的是从开始到i位置的最大子段和乘m,并没有考虑到中间某一部分乘m的情况。

最后:如何维护中间某一段元素乘m的情况呢,那就给dp数组加一个表示中间相乘就ok。

定义:dp[max][3]:dp[i][0]:表示到i为止没有乘m的最大值,dp[i][1]:表示不知道从什么时候开始,但是一直乘m乘到第i个位置(处于正在被乘的区间),dp[i][2]:前面已经有段区间乘完m了,所以现在不能乘m了。这样子的状态就考虑到了中间某一部分。

转移:

        dp[i][0] = max(dp[i-1][0]+a[i],a[i]);
        dp[i][1] = max(dp[i-1][0]+a[i]*m,max(dp[i-1][1]+a[i]*m,a[i]*m));
        dp[i][2] = max(dp[i-1][1]+a[i],max(dp[i-1][2]+a[i],a[i]));

代码如下:

/*by kzl*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>

using namespace std;
const int maxx = 1e6+500;
const int INF = 0x3f3f3f3f;
typedef long long ll;

int n,m;
ll a[maxx];
char s[maxx];
ll mi = 1e18,ma = -1*1e18;
ll dp[maxx][3];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++){
        dp[i][0] = max(dp[i-1][0]+a[i],a[i]);
        dp[i][1] = max(dp[i-1][0]+a[i]*m,max(dp[i-1][1]+a[i]*m,a[i]*m));
        dp[i][2] = max(dp[i-1][1]+a[i],max(dp[i-1][2]+a[i],a[i]));
        ma = max(ma,max(dp[i][0],dp[i][1]));
        ma = max(ma,dp[i][2]);
    }
    printf("%lld\n",max(ma,0ll));
    return 0;
}

以上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值