longpo的回文 题解
题目大意
有一个字符串和\(m\)个操作,每个操作都有一定的代价,判断是否能通过一系列操作使该字符串变成回文串。并求出最小代价。
分析
- \(Floyed\)
给出这些操作,会发现有些操作是完全没有用的(因为有更好的组合可以达到此目的),所以我们可以先跑一遍\(Floyed\),求出这26个字母与空格互相转换的最小代价。
转换为空格就是删除操作,从空格转换来就是添加操作
// 我们这里使用0~25表示a~z 用26表示空格
for ( int i = 0 ; i < 27 ; i ++ )
for ( int j = 0 ; j < 27 ; j ++ ) g[ i ][ j ] = inf ;
for ( int i = 0 ; i < 27 ; i ++ ) g[ i ][ i ] = 0 ;
for ( int i = 1 ; i <= m ; i ++ ) {
char tmp[ 10 ] , a[ 3 ] , b[ 3 ] ; int v ;
scanf ( "%s" , tmp + 1 ) ;
if ( tmp[ 1 ] == 'a' ) {
scanf ( "%s%d" , a , &v ) ;
int t = a[ 0 ] - 'a' ;
g[ 26 ][ t ] = min ( g[ 26 ][ t ] , ( ll ) v ) ;
}
else if ( tmp[ 1 ] == 'e' ) {
scanf ( "%s%d" , a , &v ) ;
int t = a[ 0 ] - 'a' ;
g[ t ][ 26 ] = min ( g[ 26 ][ t ] , ( ll ) v ) ;
}
else {
scanf ( "%s%s%d" , a , b , &v ) ;
int t1 = a[ 0 ] - 'a' , t2 = b[ 0 ] - 'a' ;
g[ t1 ][ t2 ] = min ( g[ t1 ][ t2 ] , ( ll ) v ) ;
}
}
for ( int k = 0 ; k < 27 ; k ++ ) {
for ( int i = 0 ; i < 27 ; i ++ ) {
for ( int j = 0 ; j < 27 ; j ++ ) {
g[ i ][ j ] = min ( g[ i ][ k ] + g[ k ][ j ] , g[ i ][ j ] ) ;
}
}
}
- 然后我们采用记忆化搜索
因为要构成回文串,所以我们首尾搜索,每次搜索暴力枚举首尾变成哪个字符(或空格),直到最后搜完
为了防止超时,采用记忆化
inline ll solve ( int st , int ed ) {
if ( st >= ed ) return 0 ; // 结束
if ( dp[ st ][ ed ] != -1 ) return dp[ st ][ ed ] ; // 记忆化
ll ret = inf ;
int a = val[ st ] , b = val[ ed ] ;
for ( int i = 0 ; i < 27 ; i ++ ) { // 暴力枚举每个字符
if ( g[ a ][ i ] + g[ b ][ i ] < inf )
ret = min ( ret , g[ a ][ i ] + g[ b ][ i ] + solve ( st + 1 , ed - 1 ) ) ;
if ( g[ a ][ i ] + g[ 26 ][ i ] < inf )
ret = min ( ret , g[ a ][ i ] + g[ 26 ][ i ] + solve ( st + 1 , ed ) ) ;
if ( g[ 26 ][ i ] + g[ b ][ i ] < inf )
ret = min ( ret , g[ 26 ][ i ] + g[ b ][ i ] + solve ( st , ed - 1 ) ) ;
}
return dp[ st ][ ed ] = ret ;
}