题解:AT_agc062_b [AGC062B] Split and Insert

posted on 2024-11-22 03:36:22 | under | source

考虑时光倒流,变为每次选取一个子序列移到末尾,要使得最终序列升序。

考虑最后一次操作,则选取的点集合和没选取的点集合此前应当是内部相对顺序不变的。那么分为两个子问题:让这两个集合内部升序的最小代价。

注意到此题的代价满足乘法分配率,又因为两个集合之间的顺序我们并不关心,所以假如一轮操作同时涉及了两个不同集合,就可以将其拆为两个操作,分别操作了两个集合。

那么两个子问题完全独立。现在简单了,对值域进行区间 dp,\(f_{i,l,r}\) 表示倒流完操作 \([i,k]\),其中 \([l,r]\) 范围内的元素有序的最小花费。初始化 \(f_{k+1,l,r}\) 判断下一开始 \([l,r]\) 是否有序即可,转移是朴素的不再赘述。

复杂度 \(O(n^3k)\)

代码

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define MIN(a, b) a = min(a, b)
const int N = 1e2 + 5;
int n, m, x, a[N], c[N], f[N][N][N], inf;

signed main(){
	memset(f, 0x3f, sizeof f), inf = f[0][0][0];
	cin >> n >> m;
	for(int i = 1; i <= m; ++i) scanf("%lld", &c[i]);
	for(int i = 1; i <= n; ++i) scanf("%lld", &x), a[x] = i;
	for(int i = 1; i <= n; ++i)
		for(int j = i, lst = -1; j <= n; ++j){
			if(lst > a[j]) break;
			f[m + 1][i][j] = 0;
			lst = a[j];
		}
	for(int i = m; i; --i)
		for(int l = 1; l <= n; ++l)
			for(int r = l; r <= n; ++r){
				MIN(f[i][l][r], f[i + 1][l][r]);
				for(int k = l; k < r; ++k)
					MIN(f[i][l][r], f[i + 1][l][k] + f[i + 1][k + 1][r] + c[i] * (r - k));
			}
	if(f[1][1][n] >= inf) cout << -1;
	else cout << f[1][1][n];
	return 0;
}
posted @ 2026-01-15 08:13  Zwi  阅读(3)  评论(0)    收藏  举报