0%

2547. 拆分数组的最小代价

2547.拆分数组的最小代价

题目描述:

给你一个整数数组 nums 和一个整数 k

将数组拆分成一些非空子数组。拆分的 代价 是每个子数组中的 重要性 之和。

trimmed(subarray) 作为子数组的一个特征,其中所有仅出现一次的数字将会被移除。

  • 例如,trimmed([3,1,2,4,3,4]) = [3,4,3,4]

子数组的 重要性 定义为 k + trimmed(subarray).length

  • 例如,如果一个子数组是 [1,2,3,3,3,4,4]trimmed([1,2,3,3,3,4,4]) = [3,3,3,4,4] 。这个子数组的重要性就是 k + 5

找出并返回拆分 nums 的所有可行方案中的最小代价。

子数组 是数组的一个连续 非空 元素序列。

数据范围:

$1\le nums.len \le 1000, 0 \le nums[i] \lt nums.len, 1\le k \le 10^9$

题解:

划分型dp:

两种方法,我比较常用的是设 $dp[i]$ 表示区间 $[1, i]$ 的代价, $dp[0] = 0$ ,然后枚举上一段区间的终点 $j$ , $dp[i] = \min(dp[i], dp[j] + cost[j + 1, i]), j\in[0, i - 1]$

注意如何计算 $cost[j + 1, i]$ ,因为下标变成从 $1$ 开始了,所以该区间对应到原来数组为 $[j, i - 1]$ 。

考虑如何递推计算区间代价,倒着枚举 $j$ 的话,可以递推处理,每次往最后一段区间中增加一个 $j + 1$ 。增加的数字就是 $nums[j + 1 - 1] = nums[j]$ 。

考虑计算区间中独立数字的个数,然后区间代价就是区间长度减去独立数字个数然后加上 $k$ ,即 $i - j - cost + k$ 。

第二种方法是使用 $dp[i + 1]$ 表示区间 $[0, i]$ 的代价, $dp[-1 + 1] = 0$ ,枚举最后一段区间的起点 $j$ , $dp[i + 1] = \min(dp[i + 1], dp[j - 1 + 1] + cost[j, i]), j\in[0, i]$

每次往最后一段里面加入 $nums[j]$ 。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int minCost(vector<int> &nums, int k)
{
int n = nums.size();
vector<int> dp(n + 1, INF);
// dp[i] = min(dp[i], dp[j] + cost(j + 1, i));
vector<int> vis(n);
int cost;
dp[0] = 0;
for (int i = 1; i <= n; ++i)
{
fill(vis.begin(), vis.end(), 0);
cost = 0;
for (int j = i - 1; j >= 0; --j) // 枚举上一段的终点
{
if (vis[nums[j]] == 0)
cost++;
if (vis[nums[j]] == 1)
cost--;
vis[nums[j]]++;
dp[i] = min(dp[i], dp[j] + (i - j) - cost + k);
}
}
return dp[n];
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int minCost(vector<int> &nums, int k)
{
int n = nums.size();
vector<int> dp(n + 1, INF);
// dp[i] = min(dp[i], dp[j] + cost(j + 1, i));
vector<int> vis(n);
int cost;
dp[0] = 0;
for (int i = 0; i < n; ++i)
{
fill(vis.begin(), vis.end(), 0);
cost = 0;
for (int j = i; j >= 0; --j) // 枚举上一段的终点
{
if (vis[nums[j]] == 0)
cost++;
if (vis[nums[j]] == 1)
cost--;
vis[nums[j]]++;
dp[i + 1] = min(dp[i + 1], dp[j] + (i - j + 1) - cost + k);
}
}
return dp[n];
}
};