最大权值划分
大概题目意思是给你一个数组,你需要把它划分为连续的一些子数组,使得每个子数组的max-min最小。
思路
第一眼的时候没啥思路,直接想了个O(n2)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]=max(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[i?1][0],f[i?1][1]?a[i])
- max(f[i?1][1],max(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[i?1][1],max(f[i?1][0]+a[i],f[i?1][2]?a[i]));
- f[i][0]=max(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[i?1][2],f[i?1][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;
}