Description
Bsny的书架乱成一团了,帮他一下吧!
他的书架上一共有n本书,我们定义混乱值是连续相同高度书本的段数。例如,如果书的高度是30,30,31,31,32,那么混乱值为3;30,32,32,31的混乱值也为3。但是31,32,31,32,31的混乱值为5,这实在是太乱了。
Bsny想尽可能减少混乱值,但他有点累了,所以他决定最多取出k本书,再随意将它们放回到书架上。你能帮助他吗?
100%的数据:1≤k≤n≤100,注意所有书本高度在[25,32]。
Analysis
书只有8种,启示我们可以用二进制
这种题可以感觉到用dp做
f[i][j][k][s]表示做完前i个,取了j次书(可能插回前面或后面),最后一个数(没被取走)为k,前面8种书的出现状态为s的最小混乱值
至于记录s的必要就是判断插回前面或后面答案是否要变,且一个位置后面是否有j这种书要预处理来辅助判断。否则会有后效性。
O(n2∗8∗28)
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
const int N=105,INF=2139062143;
int n,m,_2[20],a[N],f[N][N][10][260];
bool ap[N][10];
int main()
{
_2[0]=1;
fo(i,1,19) _2[i]=_2[i-1]*2;
scanf("%d %d",&n,&m);
fo(i,1,n) scanf("%d",&a[i]),a[i]-=25;
fd(i,n,1)
{
fo(j,0,7) ap[i][j]=ap[i+1][j];
ap[i][a[i]]=1;
}
memset(f,127,sizeof(f));
int ans=INF;
f[1][0][a[1]][_2[a[1]]]=1;
f[1][1][8][0]=0;
fo(i,1,n)
fo(j,0,min(i,m))
fo(k,0,8)
fo(s,0,_2[8]-1)
{
if(i>j && k==8) continue;
int x=f[i][j][k][s];
if(x==INF) continue;
int &nf=f[i+1][j][a[i+1]][s|_2[a[i+1]]];
nf=min(nf,x+1-(a[i+1]==k));
int &ng=f[i+1][j+1][k][s];
if(i+1<n) ng=min(ng,x+1-ap[i+2][a[i+1]]);
bool p=s & _2[a[i+1]];
ng=min(ng,x+1-p);
if(i+1==n) ans=min(ans,nf);
if(i+1==n && j<m) ans=min(ans,ng);
}
printf("%d",ans);
return 0;
}