版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
CF1238E Keyboard Purchase
题意
-
给定
n
和m
,给出一个由m
个不同小写字母组成的长度为n
的字符串 -
可以改变前
m
个小写字母的相对位置 -
每一种可能的值是给出的字符串的每两个相邻位置的字母的相对位置的和
- 即
-
求最小的一种排列方式
思路
显然可以考虑状压dp
每次转移为下一个加入的字符为哪个
需要考虑的是转移代价的计算
由于i
与j
的代价(
)与他加入字符串的时间有关
对于这样的问题,
我们考虑若j
不加入字符串,那么
需要向后推移一位
则代价需要加上j
字符与所有已加入字符串的字符的对数
扫描二维码关注公众号,回复:
7594253 查看本文章
dp[0] = 0;
int in[21], out[21], val[21];
register int p, q;
for (int i = 0; i < limit; i++) {
int temp = i;
p = q = 0;
for (int j = 0; j < m; j++)
if (temp & n2[j])
in[++p] = j;
else
out[++q] = j;
int ans = dp[temp];
for (int j = 1; j <= q; j++)
for (int k = 1; k <= p; k++)
ans += a[out[j]][in[k]];
int cnt;
for (int j = 1; j <= q; j++) {
cnt = temp | n2[out[j]];
dp[cnt] = min(dp[cnt], ans);
}
}
代码
#include <bits\stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, m;
char s[maxn];
int a[26][26], n2[26];
void init()
{
n2[0] = 1;
for (int i = 1; i < m; i++)
n2[i] = n2[i - 1] << 1;
for (int i = 1; i < n; i++)
if (s[i] != s[i + 1])
a[s[i] - 'a'][s[i + 1] - 'a']++;
for (int i = 0; i < m; i++)
for (int j = i + 1; j < m; j++)
a[i][j] = a[j][i] = a[i][j] + a[j][i];
}
const int maxm = 1 << 20;
int dp[maxm];
int main()
{
scanf("%d%d", &n, &m);
scanf("%s", s + 1);
init();
int limit = (1 << m) - 1;
memset(dp, 0X3f, sizeof(int) * (limit + 1));
dp[0] = 0;
int in[21], out[21], val[21];
register int p, q;
for (int i = 0; i < limit; i++) {
int temp = i;
p = q = 0;
for (int j = 0; j < m; j++)
if (temp & n2[j])
in[++p] = j;
else
out[++q] = j;
int ans = dp[temp];
for (int j = 1; j <= q; j++)
for (int k = 1; k <= p; k++)
ans += a[out[j]][in[k]];
int cnt;
for (int j = 1; j <= q; j++) {
cnt = temp | n2[out[j]];
dp[cnt] = min(dp[cnt], ans);
}
}
printf("%d\n", dp[limit]);
return 0;
}