1. 前言
和刚学的 I Love Random 一个道理,所以为了巩固赶紧又来写一篇。
2.题解
这道题和 I Love Random 一个道理,考虑构造答案序列,但是这道题和 I Love Random 有一个不一样的 Hint,就是一个非常小的贪心:假设我们现在构造了一个答案序列 p, 则 p 中同类元素的相对位置和 a 中同类元素的相对位置是不变的。
所以我们只关注在答案序列的前 i i i 个中有几个 R R R, G G G, Y Y Y 就行了。
我们定义数组 d p [ i ] [ j ] [ k ] [ z ] [ c h ] dp[i][j][k][z][ch] dp[i][j][k][z][ch] 表示构造了答案序列的前 i i i 个字符,分别有 j , k , z j, k, z j,k,z 个 R , G , Y R, G, Y R,G,Y,最后一个字符是 c h ch ch。
会爆空间,我们考虑优化。
- 发现 j + k + z = i j + k + z = i j+k+z=i,所以可以去掉 z z z,用 i − j − k i - j - k i−j−k 倒推 z z z。
- 发现第一位可以滚动,滚起来就行了。
空间就没问题了。
时间复杂度 O ( n 3 ) O (n ^ 3) O(n3) (大常数,不过还是能轻松过题)。
转移方程比较麻烦(不过不难想),这里大概讲一下思路。
考虑 d p [ i ] [ j ] [ k ] [ c h ] dp[i][j][k][ch] dp[i][j][k][ch],我们考虑在它后面加一个 R , G , Y R,G,Y R,G,Y,转移到下一个 d p dp dp,然后就结束啦。
#include <map>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); i++)
#define per(i,j,k) for (int i = (j); i >= (k); i--)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
bool read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
return 1;
}
template <typename T>
void write (T x) {
if (x < 0) {
putchar ('-');
x = -x;
}
if (x < 10) {
putchar (x + '0');
return;
}
write (x / 10);
putchar (x % 10 + '0');
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const int Maxn = 400;
const int Maxkind = 3;
const int Inf = 0x3f3f3f3f;
int n;
int dp[2 + 5][Maxn + 5][Maxn + 5][Maxkind + 5];
int st[Maxkind + 5][Maxn + 5], tp[Maxkind + 5];
char s[Maxn + 5];
int work (char x) {
if (x == 'R') return 1;
else if (x == 'G') return 2;
else if (x == 'Y') return 3;
else return -1;
}
void Change (int &x, int y) {
x = Min (x, y);
}
signed main () {
// freopen ("C:\\Users\\Administrator\\Desktop\\vscode\\1.in", "r", stdin);
// freopen ("C:\\Users\\Administrator\\Desktop\\vscode\\1.out", "w", stdout);
read (n);
scanf ("%s", s + 1);
rep (i, 1, n) {
st[work (s[i])][++tp[work (s[i])]] = i;
}
memset (dp, 0x3f, sizeof dp);
if (tp[1]) dp[1][1][0][1] = Abs (st[1][1] - 1);
if (tp[2]) dp[1][0][1][2] = Abs (st[2][1] - 1);
if (tp[3]) dp[1][0][0][3] = Abs (st[3][1] - 1);
rep (i, 1, n) {
rep (j, 0, tp[1])
rep (k, 0, tp[2])
rep (ch, 1, 3)
dp[(i & 1) ^ 1][j][k][ch] = Inf;
rep (j, 0, tp[1]) {
rep (k, 0, tp[2]) {
if (j + k - i > tp[3]) break;
if (i - j - k < 0) break;
rep (ch, 1, 3) {
int val1 = Inf; if (j + 1 <= tp[1] && ch != 1) val1 = dp[i & 1][j][k][ch] + Abs (i + 1 - st[1][j + 1]);//在后面加 R
int val2 = Inf; if (k + 1 <= tp[2] && ch != 2) val2 = dp[i & 1][j][k][ch] + Abs (i + 1 - st[2][k + 1]);//在后面加 G
int val3 = Inf; if (i - j - k + 1 <= tp[3] && ch != 3) val3 = dp[i & 1][j][k][ch] + Abs (i + 1 - st[3][i - j - k + 1]);//在后面加 Y
Change (dp[(i & 1) ^ 1][j + 1][k][1], val1);
Change (dp[(i & 1) ^ 1][j][k + 1][2], val2);
Change (dp[(i & 1) ^ 1][j][k][3], val3);
}
}
}
}
int res = Inf;
rep (i, 1, 3)
res = Min (res, dp[n & 1][tp[1]][tp[2]][i]);
write (res / 2);
return 0;
}