Educational Codeforces Round 74 E - Keyboard Purchase 状压dp

E. Keyboard Purchase

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

//预处理可以降低一个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]);
}
发布了302 篇原创文章 · 获赞 98 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/ccsu_cat/article/details/102429349