题解:P10197 [USACO24FEB] Minimum Sum of Maximums P
posted on 2025-02-18 02:54:14 | under | source
题意:给序列 \(a\),有 \(k\) 个位置固定,其它可任意交换,最小化 \(\sum \max(a_i,a_{i+1})\)。\(n\le 300,k\le 6\)。
首先,对 \(\max\) 进行讨论非常不友好,可以拆成绝对值的形式。
可以在两端设两个极大值,以规避对边界的讨论。那么现在每个 \(a_i\) 恰贡献两次,只考虑最小化 \(\sum |a_i-a_{i+1}|\)。
序列被固定元素划分为若干段,对于一段,假设已确定元素集合,考虑如何排列。记左右端固定元素为 \(L_i,R_i\),有结论:
- 记 \(L_i\le R_i\),则元素从左到右从小到大排列最优。
很符合人类知觉,证明也不难调整法即可。大概就是假如序列中间出现非法情况那么显然调整更优,假如是两端出现非法情况,简单分讨发现不会更劣。
那么该段价值 \(val(i)\) 只和最大最小元素有关:\(val(i)=|L_i-mi|+|R_i-ma|+ma-mi\)。
然后你看了看数据范围,感觉非常像是区间 dp 再套个状压。那么自然想到能不能证明集合只会不交和包含?确实是可以的。
先确立一个认知:对于一个段,假如最大值变小或最小值变大那么不劣,分讨即可。那么对于两个相交段 \(X,Y\),设 \(ma_X>mi_Y\),那么一定可以交换 \(ma_X,mi_Y\),显然不劣。
开始愉快的 dp。对序列排序,记 \(f_{l,r,S}\) 表示考虑区间 \([l,r]\) 已经填完 \(S\) 集合的段的最小代价。不过比较麻烦的是会不会留一些元素不选、而留给后面的段呢?其实不会,因为你考虑满足这个条件的两个段,那么显然是可以调整使得大段的最值不变、而小段边界缩短的。
所以呢留的元素只可能放在序列两边。转移分三类讨论:边界留元素、套一个大段、两边拼起来。非常容易就不说了。
复杂度 \(O(n^23^k+nk2^k)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define MIN(a, b) a = min(a, b)
#define myz(x) ((x) < 0 ? -(x) : (x))
const int N = 3e2 + 5, K = 7, inf = 2e6;
int _n, n, _m, m, _a[N], a[N], x[K + 5], f[N][N][1 << K], ans, Len[1 << K];
struct node{int l, r, len;} b[K];
inline int Val(int i, int mi, int ma) {return myz(b[i].l - mi) + myz(b[i].r - ma) + ma - mi;}
signed main(){
cin >> _n >> _m, ++_m;
x[_m] = _n + 1, _a[0] = _a[_n + 1] = inf;
for(int i = 1; i <= _n; ++i) scanf("%lld", &_a[i]), ans += _a[i] * 2;
for(int i = 1; i < _m; ++i) scanf("%lld", &x[i]);
for(int i = 0; i < _m; ++i){
node qwq = {_a[x[i]], _a[x[i + 1]], x[i + 1] - x[i] - 1};
if(qwq.l > qwq.r) swap(qwq.l, qwq.r);
if(!qwq.len) ans += qwq.r - qwq.l;
else b[m++] = qwq;
}
for(int i = 0; i < _m; ++i)
for(int j = x[i] + 1; j < x[i + 1]; ++j) a[++n] = _a[j];
sort(a + 1, a + 1 + n);
memset(f, 0x3f, sizeof f);
for(int i = 1; i <= n + 1; ++i) f[i][i - 1][0] = 0;
for(int i = 0; i < 1 << m; ++i)
for(int j = 0; j < m; ++j)
if((i >> j) & 1) Len[i] += b[j].len;
for(int len = 1; len <= n; ++len)
for(int l = 1; l + len - 1 <= n; ++l){
int r = l + len - 1;
for(int S = 0; S < 1 << m; ++S){
if(Len[S] > len) continue;
MIN(f[l][r][S], min(f[l + 1][r][S], f[l][r - 1][S]));
if(len == Len[S]){
for(int i = 0; i < m; ++i)
if((S >> i) & 1){
if(b[i].len > 1) MIN(f[l][r][S], f[l + 1][r - 1][S ^ (1 << i)] + Val(i, a[l], a[r]));
else if(len == 1) MIN(f[l][r][S], Val(i, a[l], a[r]));
}
}
for(int S2 = S; S2; S2 = (S2 - 1) & S)
MIN(f[l][r][S], f[l][l + Len[S2] - 1][S2] + f[l + Len[S2]][r][S ^ S2]);
}
}
cout << (ans + f[1][n][(1 << m) - 1]) / 2 - inf;
return 0;
}

浙公网安备 33010602011771号