P1777 帮助_NOI导刊2010提高(03)

也许更好的阅读体验

\(\mathcal{Description}\)

Bubu的书架乱成一团了!帮他一下吧!

他的书架上一共有n本书。我们定义混乱值是连续相同高度书本的段数。例如,如果书的高度是30,30,31,31,32,那么混乱值为3,30,32,32,31的混乱度也是3,但31,32,31,32,31的混乱度是5,这实在是太乱了。

Bubu想尽可能地减少混乱度,但他有点累了,所以他决定最多取出k本书,再随意将它们放到书架上。你能帮助他吗?

\(\mathcal{Solution}\)

考虑取出一本书把其认为是删掉这本书,因为总有办法使其不会影响到后面的取书过程
特殊的是如果将一种书全部删掉,那么应该还是会有1的混乱度
如果考虑\(dp\)方式为如何删书,那么这个过程实在复杂
如果考虑最后剩下的书,那么复杂度是\(2^n\)妥妥的\(T\)
但是仔细的想一下,会发现,考虑最后剩下的书时,并不是前面所有的状态都对现在的状态会有影响
即如果一本书隔得较远,则与你当前这本书没有多大关系,在保证所有的状态会被考虑在内的情况下,考虑状态压缩
\(f[i][j][s][l]\)表示前\(i\)本书,删掉了\(j\)本,剩下书的种类的集合为\(s\),上一本没有被删的书的种类是\(l\)

考虑第\(i\)本书

  • 若删掉这本书,则有\(f[i][j+1][s][l]=min\left(f[i][j][s][l],]f[i-1][j][s][l]\right)\)
  • 若保留这本书,则有\(f[i][j][s|(1<<h[i])][h[i]]=min\left(f[i][j][s|(1<<h[i])][h[i]],f[i-1][j][s][l]+\left(h[i]==l?0:1\right)\right)\)

最后的时候回来考虑剩下的书的种类集合,如果原本所有书的种类集合\(S\)有一种书,现在没有了,那么我们就多减了一个1,加回来就是

\(\mathcal{Code}\)

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年10月04日 星期五 08时36分03秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
#include <cstring>
#define inf 0x7f7f7f7f
using namespace std;
const int maxn = 111;
const int lim = 256;
//{{{cin
struct IO{
    template<typename T>
    IO & operator>>(T&res){
        res=0;
        bool flag=false;
        char ch;
        while((ch=getchar())>'9'||ch<'0')   flag|=ch=='-';
        while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
        if (flag)   res=~res+1;
        return *this;
    }
}cin;
//}}}
int n,k,cas;
int h[maxn],one[lim+5];
int f[2][maxn][lim+5][11];
bool now,pre;
//f[i][j][k][l] 前i本书选了j本书,存在的书的种类集合为k,上一本书为l
int main()
{
    for (int i=0;i<lim;++i){
        for (int j=0;j<8;++j)
            if (i&(1<<j))   ++one[i];
    }
    while (true){
        cin>>n>>k;
        if (n+k==0) break;
        int S=0,mh=0;
        for (int i=1;i<=n;++i){
            cin>>h[i];
            h[i]-=25;
            S|=1<<h[i];
            mh=max(mh,h[i]);
        }
        ++mh;
        memset(f[0],0x7f,sizeof(f[0]));
        now=0;
        f[0][0][1<<h[1]][h[1]]=1;
        f[0][1][0][mh]=0;
        for (int i=2;i<=n;++i){
            now^=1,pre=!now;
            memset(f[now],0x7f,sizeof(f[now]));
            for (int j=0;j<=k;++j){
                for (int s=0;s<=S;++s)
                    for (int l=0;l<=mh;++l)
                        if (f[pre][j][s][l]!=inf){
                            f[now][j][s|(1<<h[i])][h[i]]=min(f[now][j][s|(1<<h[i])][h[i]],f[pre][j][s][l]+(h[i]==l?0:1));
                            f[now][j+1][s][l]=min(f[now][j+1][s][l],f[pre][j][s][l]);
                        }
            }
        }
        int ans=inf;
        for (int j=0;j<=k;++j)
            for (int s=0;s<=S;++s)
                for (int l=0;l<mh;++l)
                    if (f[now][j][s][l]!=inf){
                        int take=S^s;
                        ans=min(ans,f[now][j][s][l]+one[take]);
                    }
        printf("Case %d: %d\n\n",++cas,ans);
    }
    return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

扫描二维码关注公众号,回复: 7426932 查看本文章

猜你喜欢

转载自www.cnblogs.com/Morning-Glory/p/11623293.html