洛谷 P3694 邦邦的大合唱站队

题目链接

Solution

\(m\) 很小,考虑状压。状态 \(S\) 一共有 \(m\) 位,每一位代表当前乐队是否排好(即乐队所有成员都站在一起),并假设所有排好的乐队都站在前面。

\(f[S]\) 表示状态为 \(S\) 时最少移动的偶像数目。不管所有排好的乐队顺序如何,它们的总数是一定的。枚举 \(S\) 状态中的每一个 \(0\),如果将它变成 \(1\) 并接在所有排好的乐队后,需要付出的代价为:所有不在指定区域内的偶像出列,所有在指定区域内不是当前乐队的偶像出列,它们进行互换。可以使用前缀和维护。

Code

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 int n, m, cnt, a, sum[26][101000], f[(1 << 20) + 666];
 
 int main()
 {
     scanf("%d%d", &n, &m);
     memset(sum, 0, sizeof(sum));
     for(int i = 1; i <= n; i++)
     {
         scanf("%d", &a);
         for(int j = 1; j <= m; j++) sum[j][i] = sum[j][i - 1];
         sum[a][i]++;
     }
     memset(f, 0x3f, sizeof(f));
     f[0] = 0;
     for(int i = 0; i <= (1 << m) - 1; i++)
     {
         cnt = 0;
         for(int j = 1; j <= m; j++)
             if(i & (1 << (j - 1))) cnt += sum[j][n];
         for(int j = 1; j <= m; j++)
             if((i & (1 << (j - 1))) == 0)
             {
                 int l = cnt + 1, r = cnt + sum[j][n], len = r - l + 1;
                 f[i | (1 << (j - 1))] = min(f[i | (1 << (j - 1))], f[i] + len - sum[j][r] + sum[j][l - 1]);
             }
     }
     printf("%d", f[(1 << m) - 1]);
     return 0;
 } 
posted @ 2020-03-14 20:13  Nyxia  阅读(82)  评论(0编辑  收藏  举报