[CFEDU74 E] Keyboard Purchase

当在使用一指禅键入一个字符串的时候,你需要不停的移动手指。比如说输入“af”,手指需要跨过3个键(包括终点)输入“a”和“f”。

针对一个经常输入的字符串,你可以定制一个长条键盘,使得在这个键盘上输入字符串需要移动的距离最短。

给出字符串长度nn与字典大小kk(小写字母的前kk个),给出最优情况下手指需要移动的距离。

分析

这道题和之前那道marbles很像……或者说状压都挺像的。

题目即依次决定键盘的下一个按键装啥,并在原字符串里计算需要移动的次数。设f(S)f(S)表示已经考虑了集合SS里所有的按键。不过仔细思考一下发现这个代价需要知道先前安装的按键的顺序,而这个顺序显然不能加入到状态里,这是无法接受的。必须要通过别的方式来计算。

然后我又卡了。

这是没见过的转移方式……考虑本轮没有排入键A,那么一定就排入了别的键B,那么,这次对于事件“排A”的一次delay会导致原串里A和相邻在键盘上已确定位置的字母间的转移多一个代价。

有种模糊的感觉。之前那个题目是计算当前元素和已有元素之间的代价;这个是计算已有元素和未有元素之间的累进式代价。搞不太清楚这个f(S)f(S)现在代表的是什么了…不知可否有人指导。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
using namespace std;
using ll=long long;
const int MAXK=21;
int nlen,klen;
string inp;

int cost[MAXK][MAXK];
ll dp[1<<20];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>nlen>>klen;
cin>>inp;

for(int i=1;i<nlen;i++){
cost[inp[i-1]-&#039;a&#039;][inp[i]-&#039;a&#039;]++;
cost[inp[i]-&#039;a&#039;][inp[i-1]-&#039;a&#039;]++;
}


memset(dp,0x3f,sizeof(dp));
dp[0]=0;

for(int i=0;i<(1<<klen);i++){
ll scost=0;
for(int j=0;j<klen;j++){
if(!((i>>j)&1))continue;
for(int k=0;k<klen;k++){
if((i>>k)&1)continue;
scost+=cost[j][k];
}
}
for(int j=0;j<klen;j++){
if((i>>j)&1)continue;
dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+scost);
}
}

cout<<dp[(1<<klen)-1]<<endl;



return 0;
}

[CFEDU74 E] Keyboard Purchase
https://blog.chenc.me/2019/10/08/cfedu74-e-keyboard-purchase/
作者
CC
发布于
2019年10月9日
许可协议