首页 > 其他分享 >abc 232 F - Simple Operations on Sequence

abc 232 F - Simple Operations on Sequence

时间:2023-01-19 23:23:09浏览次数:41  
标签:Operations 标号 abc Simple 18 元素 ++ int 花费

题意:

求把数组 a[] 变成 b[] 的最小花费。有两种操作:

  • 花费 \(X\) 把任一元素 \(+1/-1\)
  • 花费 \(Y\) 交换相邻的两个数

\(n\le 18\)

思路:

很帅的状压dp!

看到 \(n\le 18\),就很想状压!但怎么表示状态呢?排列数是阶乘级别的,怎样才能 \(O(n2^n)\) 呢?

考虑先进行所有的交换操作,再进行所有的加减操作。设进行完所有的交换操作后得到的 “理想排列” 为 A[],我们把 A[] 中的元素重新标号为 \(1,\cdots , n\),那么从 a[] 变到 A[] 需要的交换次数就等于 a[] 相对于 A[] 的逆序数

怎么数逆序数?先看标号为 \(1\) 元素后面比它标号小的数的个数,再看 \(2\) 的后面比它小的数的个数,以此类推。(也可以看前面比它大的数的个数,写法类似)

然而理想排列 A[] 是未知的,因此每个元素在 A[] 中的标号也未知,咋办?设 \(f_S\) 为把 a[] 中 \(S\) 表示的位置上的那些元素变换到 A[] 中前 \(|S|\) 个位置的最小花费。这样我们仅仅知道 \(S\) 包含哪些位置,而完全不知道这些 a[] 中的每个元素变换到了 A[] 中的具体哪个位置。现在我们要将 \(i\notin S\) 加入,那么 \(S\) 正是所有 “在 A[] 中的标号比 a[i]A[] 中的标号小的元素” 在 a[] 中的位置。

const int N = 18;
ll n, X, Y, a[N], b[N], f[1<<N];
void sol() {
    cin >> n >> X >> Y;
    for(int i = 0; i < n; i++)
        cin >> a[i];
    for(int i = 0; i < n; i++)
        cin >> b[i];
    
    memset(f, 0x3f, sizeof f);
    f[0] = 0;
    for(int S = 0; S < (1<<n); S++)
        for(int x = 0; x < n; x++)
            if(!(S >> x & 1)) {
                int invCnt = 0; for(int i = x + 1; i < n; i++)
                    if(S >> i & 1) invCnt++;
                f[S | (1<<x)] = min(f[S | (1<<x)],
                    f[S] + Y * invCnt + X * abs(a[x] - b[__builtin_popcount(S)]) );
            }
    cout << f[(1<<n) - 1];
}

标签:Operations,标号,abc,Simple,18,元素,++,int,花费
From: https://www.cnblogs.com/wushansinger/p/17062284.html

相关文章