[多校联考] S

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

会爆空间,我们考虑优化。

  1. 发现 j + k + z = i j + k + z = i j+k+z=i,所以可以去掉 z z z,用 i − j − k i - j - k ijk 倒推 z z z
  2. 发现第一位可以滚动,滚起来就行了。

空间就没问题了。

时间复杂度 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值