https://ac.nowcoder.com/acm/contest/33194/I


f[i][j] 代表前 i 个数,分成 j 段的最少代价。那么会有如下一个朴素的动态规划:dp[i][j] 我们考虑序列中在 a[i] 左边第一个大于 a[i] 的数 a[t],那么在上面的递推式中,
f[t, j]。 (相当于 a[i] 被原来的最右边那一段吸收了)
#include
using namespace std;
#define MAXN (8005)
const int INF = 1000000000;
int n;
int a[MAXN];
int f[MAXN][MAXN];
int tmp;
void solve()
{
scanf("%d", &n);
for (int i=1; i<=n; i++) scanf("%d", &a[i]);
a[0] = INF;
for (int i=0; i<=n; i++) for (int j=0; j<=n; j++) f[i][j]=INF;
f[0][1]=0;
for (int i=1; i<=n; i++) f[i][1]=max(f[i-1][1],a[i]);
for (int j=2; j<=n; j++) {
stack<pair<int, int>> st;
//f[j][j]=f[j-1][j-1]+a[j];
st.push({f[j-1][j-1], 0});
st.push({INF, j-1});
for (int i=j; i<=n; i++) {
tmp=INF;
while (!st.empty() && a[st.top().second]<=a[i]) {
auto o = st.top();
st.pop();
tmp = min(tmp, o.first);
}
auto o = st.top();
st.pop();
tmp = min(tmp, o.first);
f[i][j] = min(f[o.second][j], tmp+a[i]);
f[i][j] = min(f[i][j], f[i-1][j-1] + a[i]);
o.first = min(tmp, f[i][j-1]);
st.push(o);
st.push({INF, i});
}
}
for (int i=1; i<=n; i++) {
printf("%d\n", f[n][i]);
}
}
int main()
{
//freopen("1.txt", "r", stdin);
solve();
return 0;
}