首先不难发现最终答案中只会出现 \(c_i\) 中的数,所以可以将 \(c_i\) 离散化。
设 \(f_{i,j,k}\) 表示区间 \([l,r]\),最小值不小于 \(k\) 的最大收益,\(cnt_{i,j}\) 表示区间穿过 \(i\),且区间的 \(c\ge j\) 的区间数量。
枚举最小的位置 \(p\),则有转移:
\[f_{l,r,k}=\max\limits_{l\lt p\lt r}\left\{f_{l,p-1,k}+f_{p+1,r,k}+cnt_{p,k}\times k\right\} \]最后还要与 \(f_{l,r,k+1}\) 取个 \(\max\)。
在 DP 过程中顺便记录决策点,然后 DFS 输出。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 55, M = 4005;
int n, m;
int L[M], R[M], c[M], _[M], tot;
int f[N][N][M], val[N][N][M], pos[N][N][M];
int cnt[N][M];
int ans[N];
void print(int l, int r, int cur) {
if (l > r) return;
int mid = pos[l][r][cur = val[l][r][cur]];
ans[mid] = _[cur];
print(l, mid - 1, cur), print(mid + 1, r, cur);
}
void init(int l, int r) {
for (int i = l; i <= r; ++i)
for (int j = 1; j <= tot; ++j) cnt[i][j] = 0;
for (int i = 1; i <= m; ++i)
if (l <= L[i] && R[i] <= r)
for (int j = L[i]; j <= R[i]; ++j)
++cnt[j][c[i]];
for (int i = l; i <= r; ++i)
for (int j = tot - 1; j; --j) cnt[i][j] += cnt[i][j + 1];
}
void dp(int l, int r) {
for (int k = tot; k; --k) {
int mx = 0;
for (int p = l; p <= r; ++p) {
int tmp = f[l][p - 1][k] + f[p + 1][r][k] + cnt[p][k] * _[k];
if (tmp >= mx) mx = tmp, pos[l][r][k] = p;
}
if (mx >= f[l][r][k + 1]) f[l][r][k] = mx, val[l][r][k] = k;
else f[l][r][k] = f[l][r][k + 1], val[l][r][k] = val[l][r][k + 1];
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) scanf("%d%d%d", &L[i], &R[i], &c[i]), _[++tot] = c[i];
sort(_ + 1, _ + tot + 1), tot = unique(_ + 1, _ + tot + 1) - (_ + 1);
for (int i = 1; i <= m; ++i) c[i] = lower_bound(_ + 1, _ + tot + 1, c[i]) - _;
for (int len = 1; len <= n; ++len)
for (int l = 1; l <= n; ++l) {
int r = l + len - 1;
if (r > n) break;
init(l, r), dp(l, r);
}
print(1, n, 1);
printf("%d\n", f[1][n][1]);
for (int i = 1; i <= n; ++i) printf("%d ", ans[i]);
return 0;
}
标签:洛谷,cur,val,int,mid,P3592,print,mx
From: https://www.cnblogs.com/Kobe303/p/16830753.html