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 <bits/stdc++.h>
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;
}
  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值