Educational Codeforces Round 74 E - Keyboard Purchase 状压dp

E. Keyboard Purchase

题意:你要给字母表的前 m m m个字母排列,定义每对字母的距离: d i s t ( s i , s j ) = a b s ( i − j ) dist(s_{i}, s_{j})=abs(i-j) dist(si,sj)=abs(ij),给定一个长度为 n n n的字符串 s s s,求 ∑ i = 2 n d i s t ( s i − 1 , s i ) \sum_{i = 2}^{n}dist(s_{i-1},s_{i}) i=2ndist(si1,si)
解法:设 c n t [ s ] cnt[s] cnt[s] s s s二进制下 1 1 1的数量,设 w [ i ] [ j ] w[i][j] w[i][j]为字母 i i i和字母 j j j s s s中相邻的次数,我们逐位来填字母,设 d p [ S ] dp[S] dp[S]为前 c n t [ S ] cnt[S] cnt[S]位填的字母集合为 S S S,每个已填的字母对其他所有字母在前 c n t [ S ] cnt[S] cnt[S]位的贡献的总和的最小值为 d p [ S ] dp[S] dp[S],假设我们接下来在 c n t [ S ] + 1 cnt[S]+1 cnt[S]+1位又填了一个字母使得状态变成了 S 2 S2 S2,对于 i ∈ S 2 i \in S2 iS2 j ∉ S 2 j \notin S2 j/S2,显然又增加一次距离的贡献,那么 d p [ S 2 ] = m i n ( d p [ S 2 ] , d p [ S ] + w [ i ] [ j ] ) dp[S2]=min(dp[S2],dp[S]+w[i][j]) dp[S2]=min(dp[S2]dp[S]+w[i][j]) 2 m m 2 2^{m}m^{2} 2mm2复杂度即可解决这题

//预处理可以降低一个m

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int a[21], w[21][21], dp[1 << 20];
char s[maxn];
int main() {
    int n, m;
    scanf("%d%d%s", &n, &m, s + 1);
    for (int i = 2; i <= n; i++) {
        int u = s[i] - 'a';
        int v = s[i - 1] - 'a';
        w[u][v]++;
        w[v][u]++;
    }
    int N = 1 << m;
    for (int i = 1; i < N; i++) {
        dp[i] = 1e9;
        for (int j = 0; (1 << j) <= i; j++)
            if (i >> j & 1)
                dp[i] = min(dp[i], dp[i ^ (1 << j)]);
        for (int j = 0; (1 << j) <= i; j++)
            if (i >> j & 1)
                for (int k = 0; k < m; k++)
                    if (!(i >> k & 1))
                        dp[i] += w[j][k];
    }
    printf("%d\n", dp[N - 1]);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值