题解:[ARC126D] Pure Straight
题意分析
观察数据范围,发现 \(k\leq16\),可以考虑指数级做法。
设 \(f_{i,s}\) 表示处理到 \(a_i\),得到的升序序列的元素集合为 \(s\) 的最小操作数。如果 \(f_{i,s}\) 不可能成立,钦定 \(f_{i,s}=+\infty\)。
边界显然有 \(f_{0,\varnothing}=0\),考虑 \(f_{i,s}\) 的转移:
-
选择 \(a_i\)。
此时要求 \(a_i\in s\)。
记 \(t=s\setminus\set{a_i}\),加入 \(a_i\) 就要把 \(t\) 中大于等于 \(a_i\) 的都交换到后面去,那么
\[f_{i,s}\leftarrow\min(f_{i,s},f_{i-1,t}+\vert\set{x\in t\mid x>a_i}\vert) \]实际维护上,可以用
t&~((1<<a[i])-1)来表示 \(\set{x\in t\mid x\geq a_i}\)。因为 \(a_i\not\in t\),带不带等号无所谓。 -
不选 \(a_i\),此时对 \(s\) 没有要求。
则最终 \(a_i\) 在连续段左边或者右边,往左和往右取 \(\min\) 即可,有
\[f_{i,s}\leftarrow\min(f_{i,s},f_{i-1,s}+\min(\vert s\vert,k-\vert s\vert)) \]
答案即 \(f_{n,\set{1,2,3,\cdots,k}}\)。
AC 代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=200,K=16,inf=0x3f3f3f3f;
int n,k,a[N+1],f[N+1][1<<K|1];
int popcount(int s){
return __builtin_popcount(s);
}
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
memset(f,0x3f,sizeof(f));
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]--;
}
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int s=0;s<(1<<k);s++){
if(s>>a[i]&1){
int t=s^(1<<a[i]);
f[i][s]=min(f[i][s],f[i-1][t]+popcount(t&~((1<<a[i])-1)));
}
f[i][s]=min(f[i][s],f[i-1][s]+min(popcount(s),k-popcount(s)));
}
}
cout<<f[n][(1<<k)-1]<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号