E. Marbles
题意
给数组 a a a(值域为 [ 1 , 20 ] [1,20] [1,20]),每次可以交换相邻两个元素,问最少需要交换几次,使得相同的数字都是相邻的。
思路
值域较小,考虑状压,定义
d
p
[
i
]
dp[i]
dp[i],当前前缀已经排好的数字对应的数位对应为
1
1
1,所对应的二进制的最小花费,如
1111223434
1111223434
1111223434,
1
1
1和
2
2
2已经排好了
3
3
3和
4
4
4没有,所对应的二进制为
2
0
+
2
1
2^0+2^1
20+21
然后从小到大递推,
d
p
[
i
]
=
m
i
n
(
d
p
[
i
−
(
1
<
<
j
)
]
+
c
o
s
t
)
(
其
中
i
二
进
制
的
倒
数
第
j
位
为
1
)
dp[i]=min(dp[i-(1<<j)]+cost)(其中i二进制的倒数第j位为1)
dp[i]=min(dp[i−(1<<j)]+cost)(其中i二进制的倒数第j位为1)
关键在于如何转移
预处理
c
[
i
]
[
j
]
c[i][j]
c[i][j]:将所有值为
i
i
i,所有值
j
j
j的数字拉出来,保持相对位置不变,使得所有
i
i
i在
j
j
j前面的最小代价
d
p
[
i
]
dp[i]
dp[i]转移的时候,计算将所有的
j
j
j拉到后面所有的元素之前的代价。
/* Author : Rshs
* Data : 2019-09-18-09.59
*/
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-10;
const LL mod = 1e9+7;
const int MX = 1e6+5;
int a[MX];
LL c[25][25];//i在j前
LL p[25];
LL dp[(1<<20)+5];
int main(){
int n;cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
p[a[i]]++;
for(int j=1;j<=20;j++){
if(j==a[i])continue;
c[a[i]][j]+=p[j];
}
}
for(int i=1;i<=((1<<20)-1);i++){
LL mi=LLONG_MAX;
for(int j=0;j<20;j++){ //由各个元素转移过来
if(( i& (1<<j) ) == 0)continue;
LL sum=0,r=(1<<20)-1-(LL)i; //r为后面的1
for(int k=0;k<20;k++){ //拉到前面
if(r & (1<<k)) sum+=c[j+1][k+1];
}
mi=min(mi,dp[i-(1<<j)]+sum);
}
dp[i]=mi;
}
cout<<dp[(1<<20)-1]<<'\n';
return 0;
}