m
m
m很小,考虑状压
状态
S
S
S表示集合
f
[
S
]
f[S]
f[S]表示集合中偶像需要出队的最少人数
考虑花费
把偶像移到同一个区间
设现在枚到
l
e
n
len
len,每个区间最终偶像人数
n
u
m
[
i
]
num[i]
num[i]
那么最终偶像区间为
[
l
e
n
−
n
u
m
[
i
]
+
1
,
l
e
n
]
[len-num[i]+1,len]
[len−num[i]+1,len]
花费不在该区间内偶像的人数:
n
u
m
[
i
]
−
p
r
e
[
l
e
n
]
+
p
r
e
[
l
e
n
−
n
u
m
[
i
]
]
num[i]-pre[len]+pre[len-num[i]]
num[i]−pre[len]+pre[len−num[i]],其中
p
r
e
pre
pre为前缀和
考虑转移:
f
[
S
]
=
min
i
∈
S
{
f
[
S
−
i
]
+
n
u
m
[
i
]
−
p
r
e
[
l
e
n
]
+
p
r
e
[
l
e
n
−
n
u
m
[
i
]
]
}
f[S]=\min_{i\in S}\{f[S-i]+num[i]-pre[len]+pre[len-num[i]]\}
f[S]=i∈Smin{f[S−i]+num[i]−pre[len]+pre[len−num[i]]}
其中 l e n len len是只考虑这个集合中偶像个数之和
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),f=-1;
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int N=2e6+5;
const int INF=2147483647;
int n,m,pre[N][50],num[50],f[N];
int main(){
n=in,m=in;
for(int i=1;i<=n;++i){
int id=in;
for(int j=1;j<=m;++j) pre[i][j]=pre[i-1][j];
++pre[i][id];
++num[id];
}
for(int i=1;i<(1<<m);++i) f[i]=INF;
for(int S=1;S<(1<<m);++S){
int len=0;
for(int i=1;i<=m;++i)
if(S&(1<<(i-1)))
len+=num[i];
for(int i=1;i<=m;++i){
if(S&(1<<(i-1))){
f[S]=min(f[S],f[S^(1<<(i-1))]-pre[len][i]+num[i]+pre[len-num[i]][i]);
}
}
}
printf("%d\n",f[(1<<m)-1]);
return 0;
}