AtCoder Regular Contest 126 D题题解

思路

首先我们看看假设选中 m m m 个数后的答案。

我们首先现将 m m m 个数移动到一起,在将他们重新排序。

我们知道, m m m 个数移在一起时,当位于中间的那个数不动时交换次数最少,于是可以列出式子( c i c_i ci 是点 i i i 的位置):

∑ i = 1 m ∣ c m i d + m i d − c i + i ∣ \sum_{i = 1}^m |c_{mid} + mid - c_i + i| i=1mcmid+midci+i

我们可以将上面的式子改成如下形式:

− 2 m ∗ m i d + m % 2 ∗ c m i d + ∑ i = 1 m c i − 1 i < = m i d -\dfrac{2}{m}*mid + m \% 2 * c_{mid} + \sum_{i = 1}^m c_i^{-1^{i <=mid}} m2mid+m%2cmid+i=1mci1i<=mid

此时我们就可以用壮压DP来做了。

我们首先枚举每个数,在枚举选上这个数后的情况,在DP的过程中计算出下面的式子的求和公式里面的值,前面的为常数,并且在加上逆序对个数就可以了。

代码

#include 
using namespace std;
int n, m, mid, a[205], f[205][1 << 18], INF = 1e9;
int solve(int state, int i) {
    int sum = 0, t = 0, t1 = 0;
    //t是目前选了多少个数,t1选了的树中比这个数要小的数。
    for (int j = 0; j < m; j++) {
        if (state & (1 << j))
            t++;
        if (a[i] - 1 == j)
            t1 = t;
    }
    return i * (t <= mid ? -1 : 1) + i * (m & 1) * (mid == t) + (t - t1);
    //此时的i就是c值,于是我们把他带进去式子就可以了。
}
int main() {
    scanf("%d%d", &n, &m), mid = (m + 1) / 2;
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    memset(f, 36, sizeof(f));
    for (int i = 0; i <= n; i++) f[i][0] = 0;
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < 1 << m; j++)
            f[i][j] = min(j & (1 << (a[i] - 1)) ? f[i - 1][j ^ (1 << (a[i] - 1))] + solve(j, i) : INF, f[i - 1][j]);
    printf("%d", f[n][(1 << m) - 1] - m / 2 * mid);
    return 0;
}

你可能感兴趣的:(题解,c++,题解,Atcoder,ARC,ARC126)