1006-BBQ
题目大意:
给定一个字符串,每次操作可以删除或是插入一个字符,要求操作若干次后字符串从头开始,每四个一组,每组都符合"abba"的形式,问最少操作几次。
思路:
区间dp,dp[i]表示使区间[1,i]成为合法区间所需的最少操作次数。
转移的方程为
dp[i+k] = dp[i] + cal(i+1, i+k) k=[1,7]
向后转移的时候最多只要枚举长度为7的区间即可,因为将长度为8以上的区间变成合法区间的代价一定会大于长度在7以下的区间。
那么现在的问题就是如何计算区间[i+1, i+k]的代价了。
可以发现对于一个长度为4的区间,abcd和efgh是等价的,二者变成合法区间的代价相同。
那么我们可以对一个子区间进行编码,最大长度为7,因此最多出现7种字符加上空字符一共8种。采用8进制,字母出现的顺序从小到达编码。
接下来就是预处理各种区间编码合法化的代价了。
因为编码种类不多,直接暴力+二维dp求解即可:
枚举每一种编码,枚举每一种合法区间的编码,计算将当前编码转化成合法编码的代价。
AC代码:
#include <bits/stdc++.h>
const int N = 1e6 + 10;
using namespace std;
int t[10]; // 当前序列
int g[8][5]; // g[i][j]表示当前序列的前i个转化成合法序列的前j个所需的最小操作数
int w[N]; // 合法化代价
int dp[N];
char s[N];
void dfs(int n, int c) //区间长度为n,最大编码为c
{
int m;
if (n)
{
m = n; //全部删除的代价
for (int a = 1; a <= 7; a++)
{
for (int b = 1; b <= 7; b++)
{
int p[5] = {
0, a, b, b, a}; //合法序列
memset(g, 0x3f, sizeof(g));
for (int i = 0; i <= 4; i++)
g[0][i] = i;
for (int i = 0; i <= 7; i++)
g[i][0] = i;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= 4; j++)
g[i][j] = min({
g[i - 1][j] + 1, g[i][j - 1] + 1, g[i - 1][j - 1] + (t[i] != p[j])});
//转移的两种情况:
// 1、t[i]!=p[j],从(i+1,j)的情况中删去一个字符或是从(i-1,j)的情况中插入一个字符,代价为1
// 2、t[i]==p[j],代价为0
if (g[n][4] < m) m = g[n][4];
}
}
}
if (n)
{
int idx = 0;
for (int i = 1; i <= n; i++)
idx = idx * 8 + t[i];
w[idx] = m;
}
if

本文探讨了两个问题:一是通过插入或删除操作使字符串符合特定形式的最少步骤;二是从给定序列中选取一定数量的元素,这些元素在构成的环状结构中不相邻的组合数。
最低0.47元/天 解锁文章
866

被折叠的 条评论
为什么被折叠?



