懵逼题,一度推出六维的DP,最后看了题解。。
恍然大悟。。。(需要运用好题目的限制(a[i]>=25 且a[i]<=32))并将相同的a[i]进行压缩,压缩成一个值
因为拿出一本书只有两种可能,(1)放到最前面,(2)放到与它相同编号的书的旁边,那么我们可以就此加上限制,就可以推出状态转移方程式了(PS:每次拿书必须是一团一团地取出来,否则并不改变原状态)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define ll long long using namespace std; ll n,k,a[10005],h[100005],x,ans,flag[100005],sum[10005],f[2][105][10][1<<8],tot,all,p[10005]; int main(){ scanf("%lld%lld",&n,&k); for (int i=1; i<=n; i++) scanf("%lld",a+i),a[i]-=24; for (int i=1; i<=n; i++) { if (a[i]!=a[i-1]) h[++tot]=a[i]; sum[tot]++; } for (int i=tot; i; i--){ if (flag[h[i]]) p[i]=1; flag[h[i]]=1; } memset(f[0],127,sizeof(f[0])); f[0][0][0][0]=0; all=(1<<8)-1; for (int i=1; i<=tot; i++) { x=x^1; memset(f[x],127,sizeof(f[x])); for (int j=0; j<=k; j++) for (int w=0; w<=8; w++) for (int m=0; m<=all; m++) { if (f[x^1][j][w][m]>n) continue; f[x][j][h[i]][m|(1<<(h[i]-1))]=min(f[x][j][h[i]/*当前的最后一位是h[i]*/][m|(1<<(h[i]-1))],f[x^1][j][w][m]+(h[i]!=w));//当前的状态等于之前的状态+(判断此时扫描的位是否等于原先的最后一位) if (j+sum[i]>k) continue; f[x][j+sum[i]][w][m|(1<<(h[i]/*将所有的相同值的合并后得到的*/-1)/*h[i]-1表示h[i]是否被取过了,与mor一下表示当前的状态*/)]=min(f[x][j+sum[i]][w][m|(1<<(h[i]-1))],f[x^1][j][w][m]+!(m&(1<<(h[i]-1)))); if (p[i]) f[x][j+sum[i]/*改变为j+sum[i]次之后的状态*/][w][m]=min(f[x][j+sum[i]/*改变为j+sum[i]次之后的状态*/][w][m],f[x^1][j][w][m]); } } //每次x进行^操作是为了创造出滚动数组,即本次的状态只跟上一次的状态有关 ans=124278904761894269; for (int i=0; i<=k; i++) for (int w=0; w<=8; w++) for (int m=0; m<=all; m++) ans=min(ans,f[x][i][w][m]); printf("%lld\n",ans); return 0; }
时间: 2024-10-13 13:22:24