LOJ2687 BalticOI2013 Vim 线头DP
多图警告!!!
一种很新奇的,全网似乎只有一两篇题解……
首先,序列中的一段等价于在跳的过程中这一段之后的一个字符必须要经过,并且在最后的答案中加上e的个数。
那么原题等价于:给出一个序列和两种移动方式,移动过程中必须要经过某一些点,求最小代价。
我们不妨把若干连续的操作和若干连续的操作看成线,那么移动路线就变成下面这样
首先,考虑下面两种移动路线
A路线一定没有B路线优,因为A路线有重复的折返。
这样说来:如果经过某些连续的操作之后开始进行操作,那么一定会到达要到达的最前面的目标,然后一直进行操作不再回来。
到这里不难设计出一个暴力的:设表示已经经过了前个必经字符,当前光标在第个字符时的最小代价。设字符集为,那么这种是的,不够优秀。考虑优化。
发现上面的条件等价于对于某一个位置,经过的位置覆盖了位置与之间的线段的线的数量要么是,要么是,对应下图的两种情况。
到了这里就可以开始设计更加优秀的了
设表示覆盖了与之间的线段次,且覆盖与之间的线段的操作选择的字符是的最小代价,表示覆盖了与之间的线段次,且在进行操作之前覆盖与之间的线段的操作选择的字符是、在进行操作之后覆盖与之间的线段的操作选择的字符是的最小代价
又设表示字符串的第个字符,表示原串中第个字符前是否存在字符
转移:
的转移分别对应下图的情况
其中虚线表示新加入的线,红色字表示对应位置的字符类型,黑色字表示位置编号
转移分别对应下图中的情况
可以发现转移就是把线延长和补全的过程,所以叫做线头DP
初始值:,其他等于。最后的答案是,其中是没有在字符串中出现过的字符。这可以理解成在无限远的地方有一个字符,最后一次操作就是直接跳到这一个无限远的地方。当然,这意味着最后的答案会加上跳到这个无限远的地方的的代价,减掉就行了。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
//This code is written by Itst
using namespace std;
inline int read(){
int a = 0;
char c = getchar();
bool f = 0;
while(!isdigit(c) && c != EOF){
if(c == '-')
f = 1;
c = getchar();
}
if(c == EOF)
exit(0);
while(isdigit(c)){
a = a * 10 + c - 48;
c = getchar();
}
return f ? -a : a;
}
const int MAXN = 7e4 + 7 , A = 11;
int f[MAXN][A] , g[MAXN][A][A] , ch[MAXN];
bool must[MAXN];
int N , M , cnt;
inline char getc(){
char c = getchar();
while(!islower(c))
c = getchar();
return c;
}
int main(){
#ifndef ONLINE_JUDGE
//freopen("in","r",stdin);
//freopen("out","w",stdout);
#endif
N = read();
bool ife = 1;
for(int i = 1 ; i <= N ; ++i){
char c = getc();
if(c == 'e')
cnt += (ife = 1);
else{
must[++M] = ife;
ife = 0;
ch[M] = c - 'a';
}
}
for(int i = 0 ; i < A ; ++i){
for(int j = 0 ; j < A ; ++j)
g[0][i][j] = INF;
f[0][i] = INF;
}
f[0][ch[1]] = 0;
for(int i = 1 ; i <= M ; ++i)
for(int j = 0 ; j < A ; ++j){
int t = INF;
if(j != ch[i] && !must[i])
t = min(t , f[i - 1][j]);
t = min(t , f[i - 1][ch[i]] + 2);
if(j != ch[i])
t = min(t , g[i - 1][ch[i]][j]);
t = min(t , g[i - 1][ch[i]][ch[i]] + 2);
f[i][j] = t;
for(int k = 0 ; k < A ; ++k){
t = INF;
if(j != ch[i])
t = min(t , f[i - 1][j] + 3);
t = min(t , f[i - 1][ch[i]] + 5);
if(j != ch[i] && k != ch[i])
t = min(t , g[i - 1][j][k] + 1);
if(j != ch[i])
t = min(t , g[i - 1][j][ch[i]] + 3);
if(k != ch[i])
t = min(t , g[i - 1][ch[i]][k] + 3);
t = min(t , g[i - 1][ch[i]][ch[i]] + 5);
g[i][j][k] = t;
}
}
cout << f[M][10] + 2 * cnt - 2;
return 0;
}
分类:
动态规划
, From——BalticOI
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】