Codeforces 1215E 状压DP

ゞ 浴缸里的玫瑰 2023-08-17 17:22 275阅读 0赞

题意:给你一个序列,你可以交换序列中的相邻的两个元素,问最少需要交换多少次可以让这个序列变成若干个极大的颜色相同的子段。

思路:由于题目中的颜色种类很少,考虑状压DP。设dp[mask]为把mask为1的颜色从后往前放置的最小花费。那么我们新添加一种颜色时需要知道要转移多少次,所以我们需要预处理转移矩阵c[i][j]。c[i][j]的意思是只考虑i, j两种元素,把所有的元素i移动到元素j前面的最小花费,预处理好之后暴力转移即可。

代码:

  1. #include <bits/stdc++.h>
  2. #define LL long long
  3. using namespace std;
  4. const int maxn = 400010;
  5. vector<int> b[20];
  6. int a[maxn];
  7. LL f[1 << 20];
  8. LL c[20][20];
  9. int main() {
  10. int n;
  11. scanf("%d", &n);
  12. memset(f, 0x3f, sizeof(f));
  13. for (int i = 1; i <= n; i++) {
  14. scanf("%d", &a[i]);
  15. b[a[i] - 1].push_back(i);
  16. }
  17. for (int i = 0; i < 20; i++) {
  18. if(!b[i].size()) continue;
  19. for (int j = 0; j < 20; j++) {
  20. if(!b[j].size() || i == j) continue;
  21. int p = 0;
  22. for (int k = 0; k < b[i].size(); k++) {
  23. while(p < b[j].size() && b[j][p] < b[i][k]) p++;
  24. // if(p < b[j].size())
  25. c[i][j] += p;
  26. }
  27. }
  28. }
  29. f[0] = 0;
  30. for (int i = 0; i < (1 << 20); i++) {
  31. vector<int> d;
  32. d.clear();
  33. for (int j = 0; j < 20; j++) {
  34. if((i >> j) & 1) {
  35. d.push_back(j);
  36. }
  37. }
  38. for (int j = 0; j < 20; j++) {
  39. if(((i >> j) & 1) == 0) {
  40. LL sum = 0;
  41. for (int k = 0; k < d.size(); k++) {
  42. sum += c[d[k]][j];
  43. }
  44. f[i ^ (1 << j)] = min(f[i ^ (1 << j)], f[i] + sum);
  45. }
  46. }
  47. }
  48. printf("%lld\n", f[(1 << 20) - 1]);
  49. }

转载于:https://www.cnblogs.com/pkgunboat/p/11533983.html

发表评论

表情:
评论列表 (有 0 条评论,275人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Codeforces 1215E DP

    题意:给你一个序列,你可以交换序列中的相邻的两个元素,问最少需要交换多少次可以让这个序列变成若干个极大的颜色相同的子段。 思路:由于题目中的颜色种类很少,考虑状压DP。设dp

    相关 group dp

      应某些人要求,我把标签删掉了   这是一道好题。   一看$c<=16$果断状压,但是怎么压?   一个很显然的思路是,枚举上下两层的状态,每一层的状态极限有$C(c

    相关 dp(瞎BB)

    最近在写状压dp,写得不太顺利啊,抠很久才抠出来。可见如此之菜。 状态压缩dp(简称状压dp)是一种非常典型的动态规划,通常使用在NP问题的小规模求解中,虽然是指数