CF1839D题解

  • 分析

    啊这道题就做得很难受了……

    手玩一下样例,不难发现答案就是分出k段不是单调上升序列的序列,求这些序列的最小长度和。
    显然有状态fl,r,k表示[l,r]序列分成k段的最小长度和。
    转移很好想,即枚举xy分别表示左区间的右端点以及段数,空间复杂度O(n3),时间复杂度O(n5),显然不行。

    对于这种状态中存在取的数量的题目有一个常见trick,就是假设最后一个必取然后根据上一个转移,所以这样状态就变成了fl,r,k表示必取r在最后一段端点的最小长度和,转移就枚举一个x即可。

    然后还要优化一维,我们发现我们转移时右区间的答案只会等于x+1r1这个区间的大小(当且仅当ax<ar时),除此之外没有任何值,不需要任何决策就能确定,所以我们只需要算左区间就行了,这样就能把这个区间DP优化为线性DP。
    线性DP的状态fr,k表示右端点为r,分k段的最小长度和,转移同上,这样空间复杂度为O(n2)、时间复杂度为O(n3),可以通过本题。

    然后我们发现可能有时候最优的决策r不是最后一段的端点而是就在最后一段,所以我们的答案是min(mini=1r1fi,k1+ri,fn,k)

    边界条件是k<0不合法,k=0如果到r为止是单调上升的,那么合法,否则不合法。
    想到可能一个数的前缀都比他大,这时显然可以划出一个从第一个数到这个数的序列,但是根据我们的转移条件无法转移,所以将最小的r确定为0(或者可以看做一个数x转移的不是单调上升子序列区间为x+1r1,而x+1最小值为1,所以x的最小值为0),当r=0时,值也为0。

  • 代码

#include <iostream>
using namespace std;
constexpr int MAXN(507);
constexpr int INF(0x3f3f3f3f);
int vis[MAXN][MAXN], f[MAXN][MAXN];
int a[MAXN];
int T, n;
inline void read(int &temp) { cin >> temp; }
int dfs(int r, int x) {
	if (x < 0)  return INF;
	if (vis[r][x])  return f[r][x];
	vis[r][x] = 1;
	if (r == 0)  return 0;
	int res(INF);
	for (int i(0); i < r; ++i) {
		if (a[i] < a[r] && i != r - 1)  res = min(res, dfs(i, x - 1) + (r - 1) - (i + 1) + 1);
		else if (a[i] < a[r])  res = min(res, dfs(i, x));
	}
	return f[r][x] = res;
}
inline void work() {
	for (int i(0); i <= n; ++i)
		for (int j(0); j <= n; ++j)  vis[i][j] = f[i][j] = 0;
	read(n);
	for (int i(1); i <= n; ++i)  read(a[i]);
	for (int i(1); i <= n; ++i) {
		int ans = dfs(n, i);
		for (int j(1); j < n; ++j)  ans = min(ans, dfs(j, i - 1) + n - j);
		cout << ans << " ";
	}
	cout << endl;
}
int main() {
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	read(T);
	while (T--)  work(); 
	return 0;
}
posted @   Kazdale  阅读(240)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示