代码源每日一题 div1 #709. 最大权值划分

最大权值划分

大概题目意思是给你一个数组,你需要把它划分为连续的一些子数组,使得每个子数组的max-min最小。

思路

第一眼的时候没啥思路,直接想了个 O ( n 2 ) O(n^2) O(n2)的做法,也就是枚举最后一个子数组划分到哪里,也不懂咋优化。

正解

观察到最大减最小(极差问题),等价于给一个子数组里面的两个数字,一个给一个+号,一个给一个-号,成对赋予了这些符号后,整个数组的极差最大。

那么可以先不考虑怎么划分,想象最优解,数组中的正负号形态肯定也是像上面所说的那样。即 . . + . . . . . − . . − . . . + . . . + . . . − . . . ..+.....-..-...+...+...-... ..+..........+...+......这样子的一个形态。那么便豁然开朗了起来,我们便不需要考虑怎么划分,只需要使得给这些数字添上+/-号之后,使得极差最大即可。找到最优解后,只有一对+,-号所构成的子数组中,+号那个数必然是最大值,-号那个数字必然是最小值,若不是,则能找到一个更大/更小的值,即能找到一组更优的解,这和最优解矛盾!

有了上面的思想,我们来考虑设计状态,考虑到+,-号都是成对出现的,1~i这个前缀中, − , + -, + ,+之间的差的有1,0,-1三种可能

所以,状态表示为 f [ i ] [ 3 ] f[i][3] f[i][3]

  • f [ i ] [ 0 ] f[i][0] f[i][0]表示-和+之间的差为-1
  • f [ i ] [ 0 ] f[i][0] f[i][0]表示-和+之间的差为0
  • f [ i ] [ 0 ] f[i][0] f[i][0]表示-和+之间的差为1

状态转移

  • f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 1 ] − a [ i ] ) f[i][0]=max(f[i - 1][0], f[i - 1][1] - a[i]) f[i][0]=max(f[i1][0],f[i1][1]a[i])
  • m a x ( f [ i − 1 ] [ 1 ] , m a x ( f [ i − 1 ] [ 0 ] + a [ i ] , f [ i − 1 ] [ 2 ] − a [ i ] ) ) ; max(f[i - 1][1], max(f[i - 1][0] + a[i], f[i - 1][2] - a[i])); max(f[i1][1],max(f[i1][0]+a[i],f[i1][2]a[i]));
  • f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 2 ] , f [ i − 1 ] [ 1 ] + a [ i ] ) ; f[i][0]=max(f[i - 1][2], f[i - 1][1] + a[i]); f[i][0]=max(f[i1][2],f[i1][1]+a[i]);

这题状态转移和状态表示不难想,关键是如何想到如何把一个极差问题转化为添加正负号,证明这与划分等价。(似乎最大值减最小值一般都是最大值添加正号,最小值添加负号这个套路),比如子串的最大差这个题。

code

#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long LL;
using tp = tuple<int,int,int>;
typedef pair<int,int> PII;
const int N = 1e6 + 10, M = 3010, mod = 998244353, INF = 1e9;
bool multi = false;


int n;
LL f[N][3];

void solve() {
    
    scanf("%d", &n);
    f[0][1] = 0, f[0][0] = f[0][2] = -INF;
    for(int i = 1; i <= n; i++) {
        int x;
        scanf("%d", &x);

        f[i][0] = max(f[i - 1][0], f[i - 1][1] - x);
        f[i][1] = max(f[i - 1][1], max(f[i - 1][0] + x, f[i - 1][2] - x));
        f[i][2] = max(f[i - 1][2], f[i - 1][1] + x);
    }

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

int main() {
#ifdef ONLINE_JUDGE
#else 
    freopen("D.txt", "r", stdin);
#endif
//     ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    int T = 1;
    if(multi) cin >> T;
    while (T--) solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值