随便说点
这周继续补namomonamomonamomo上周的题(苣蒻落泪)。之前在AcwingAcwingAcwing上面学过区间dpdpdp但多少有点板子题的感觉,这题算是给了一个新的切入点。看完题解之后才发现自己怎么这么简单的题目都不会做(苣蒻二落泪)。
题意
给出一个数组,一次操作可以将某一个位置相邻的连续的数字全部转化为另一个相同的数字。请问最少需要操作多少次可以使数组中所有数字相同?
题解
拿到这题我们首先能够明确一个很简单的事实:最多经过n−1n-1n−1次操作我们就能达到我们需要的目标,也就是不考虑连续所有数字除了目标数字都被操作一次,那就成了n−1n-1n−1。
然后我们再来看一下什么情况下能够减少操作的次数——连续。只要我们尽可能多的产生连续的子串我们就能减少操作次数。原生的连续子串我们不用太过于关心,我们应该关心的是我们如何操作才能够减少操作次数。选择一个区间(区间?难道是区间dpdpdp?)a1 a2 … an−1 ana_1\ a_2\ \ldots\ a_{n-1}\ a_na1 a2 … an−1 an,当我们操作a2 … an−1a_2\ \ldots\ a_{n-1}a2 … an−1时我们有一点很明确就是经过一系列操作之后这个区间要么等于a1a_1a1要么等于ana_nan,只要等于其中一者我们就能将答案控制在≤n−1\leq n-1≤n−1的范围内,那如果等于两者呢?我们就可以少操作一次。所以我们知道的是我们要尽量选择端点相同的区间,每匹配成功一个区间我们的答案就可以−1-1−1,所以我们只要找出区间中最多有多少对匹配就能知道答案了。
然后我们只要计算区间中所有的数字出现了几次然后输出就能WAWAWA了。
啊对,你找到数字对数然后就会直接WAWAWA,因为题目中有一种情况是禁止的。假设存在一个数组2 3 2 32\ 3\ 2\ 32 3 2 3,如果按着我们上面说的直接GGG,因为以222为端点的区间和以333为端点的区间相互交错。
我们需要的是dpdpdp。列出区间dpdpdp数组,dp[l][r]dp[l][r]dp[l][r]代表左区间lll和右区间rrr之间有多少配对的数字?
列出状态转移方程
dp[l][r]=max(dp[l−1][r],max(dp[l+1][x−1]+dp[x][r]+1)) w[l]==w[x]
dp[l][r]=max(dp[l-1][r],max(dp[l+1][x-1]+dp[x][r]+1))\ w[l]==w[x]
dp[l][r]=max(dp[l−1][r],max(dp[l+1][x−1]+dp[x][r]+1)) w[l]==w[x]
dp[l−1][r]dp[l-1][r]dp[l−1][r]代表左端点没有被匹配。
max(dp[l+1][x−1]+dp[x][r]+1)(w[l]==w[x])max(dp[l+1][x-1]+dp[x][r]+1)(w[l]==w[x])max(dp[l+1][x−1]+dp[x][r]+1)(w[l]==w[x])是枚举区间中与左端点相同的位置尝试这个区间中有多少配对的点。
当然如果遍历区间中所有的点然后判断其是否与左区间相等O(n3)O(n^3)O(n3)绝对是要时间超限的,所以还要记录一下每个节点的下一个与之相同的节点方便跳指针。
#include<bits/stdc++.h>
using namespace std;
int main(){
#ifdef DEBUG
freopen("/Users/chenzhiyuan/Desktop/ACM/ACM/in.txt", "r", stdin);
#endif
ios_base::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int _;
cin >> _;
while(_ --){
int n;
cin >> n;
vector<int> w(n + 10);
for (int i = 1;i <= n;i ++) cin >> w[i];
vector<int> nxt(n + 10);
vector<int> pos(n + 10);
for (int i = 1;i <= n;i ++) pos[i] = n + 1;
for (int i = n;i >= 1;i --) {
nxt[i] = pos[w[i]];
pos[w[i]] = i;
}
vector<vector<int>> dp(n + 10,vector<int> (n + 10));
for (int len = 1;len <= n;len ++) {
for (int l = 1;l <= n;l ++) {
int r = l + len - 1;
if(r > n) break;
dp[l][r] = dp[l + 1][r];
int x = nxt[l];
while(x <= r) {
dp[l][r] = max(dp[l][r], dp[l + 1][x - 1] + dp[x][r] + 1);
x = nxt[x];
}
}
}
cout << n - 1 - dp[1][n] << endl;
}
return 0;
}