E. Keyboard Purchase
题意:
输入
n
(
1
e
5
)
,
m
(
20
)
n(1e5),m(20)
n(1e5),m(20);
第二行输入一个长度为
n
n
n的字符串
s
s
s(有前
m
m
m个小写字母组成)。
前
m
m
m个字母可以形成一个排列,设
p
o
s
x
pos_x
posx表示字母
x
x
x所在的位置。
求
m
i
n
(
∑
i
=
2
n
a
b
s
(
p
o
s
s
i
−
1
−
p
o
s
s
i
)
)
min(\sum_{i=2}^{n}abs(pos_{s_{i-1}}-pos_{s_i}))
min(∑i=2nabs(possi−1−possi))。
题解:
状压dp
每次加入第几个元素算是它的位置,然后把它的贡献全部加上,按状态转移求最小值。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=(1<<20)+9;
int n,m;
int d[29][29];
int dp[N];
int main(){
// freopen("tt.in","r",stdin),freopen("tt.out","w",stdout);
string s;
cin>>n>>m>>s;
for(int i=0;i<n-1;i++){
int x=s[i]-'a'+1,y=s[i+1]-'a'+1;
d[x][y]++,d[y][x]++;
}
memset(dp,0x3f,sizeof(dp)),dp[0]=0;
for(int i=0;i<(1<<m);i++){
for(int j=1;j<=m;j++)if((i&(1<<j-1))==0){
int cnt=0,sum=0,res=0;
for(int k=1;k<=m;k++)if(i&(1<<k-1)){
cnt++,sum+=d[j][k];
}else res+=d[j][k]*(j!=k);
dp[i|(1<<j-1)]=min(dp[i|(1<<j-1)],dp[i]+(cnt+1)*(sum-res));
}
}
cout<<dp[(1<<m)-1]<<endl;
return 0;
}