CF1238E Keyboard Purchase
题目大意
给定 n n n和 m m m,表示给出一个长度为 n n n的小写字母串,其中的每个字母的大小不会超过第 m m m个小写字母
前 m m m个小写字母: a , b , c , . . . a, b, c, ... a,b,c,...( m m m个)
然后可以改变前 m m m个小写字母的相对位置,有 m ! m! m!种可能,每一种可能的值是给出的字符串的每两个相邻位置的字母的相对位置的和
求这个最小值是多少
题解
一开始一直以为要把相对位置表示出来才行,然后发现自己naive了
考虑状压dp, f [ S ] f[S] f[S]表示被选入的字母集合为 S S S
考虑如何转移
∣ p o s i − p o s i + 1 ∣ |pos_{i} - pos_{i+1}| ∣posi−posi+1∣ 即两个相对位置的差可以看做 s t [ i ] st[i] st[i] 和 s t [ i + 1 ] st[i+1] st[i+1]这两个字母被选入集合的时间差
然后每次转移的贡献就是集合内的字母和集合外的字母相邻的对数
然后DP即可
code:
#include<bits/stdc++.h>
using namespace std;
int n, m, mp[25][25], f[1 << 22], g[1 << 22];
char st[100005];
int main() {
scanf("%d%d", &n, &m);
scanf("%s", st + 1);
for(int i = 2; i <= n; i ++) {
mp[st[i] - 'a'][st[i - 1] - 'a'] ++;
mp[st[i - 1] - 'a'][st[i] - 'a'] ++;//预处理两个字母相邻的对数
}
for(int S = 0; S < (1 << m); S ++)
for(int i = 0; i < m; i ++)
if((1 << i) & S)
for(int j = 0; j < m; j ++)
if(!((1 << j) & S))
g[S] += mp[i][j];//算S集合内的字母与集合外的字母相邻的对数
memset(f, 0x3f, sizeof f);
f[0] = 0;
for(int S = 0; S < (1 << m); S ++)
for(int i = 0; i < m; i ++)//把i选入集合
if(S & (1 << i)) f[S] = min(f[S], f[S ^ (1 << i)] + g[S ^ (1 << i)]);//转移
printf("%d", f[(1 << m) - 1]);//输出即可
return 0;
}
智力康复ing
妙啊!!