帮助BSNY(状态压缩+4维dp+滚动数组)

懵逼题,一度推出六维的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;
}

 

posted @ 2017-09-28 08:30 最弱的蒟蒻 阅读(...) 评论(...) 编辑 收藏