洛谷 P1777 帮助_NOI导刊2010提高(03) 解题报告

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

题目描述

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

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

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

输入输出格式

输入格式:

最多会有20组测试数据。每组测试数据开头为两个整数n,k(l≤k≤n≤100),表示总共有n本书,最多可以进行k次搬书操作。接下来一行有n个整数,表示每本书的高度,从左到右。每本书的高度是25到32间的整数。最后一组数据后有一行n=k=0。

输出格式:

对于每一组数据,输出Case标号和最终最小的混乱度。在每组数据后打印一个空行。


我们发现,对于拿出去的书是可以随便放的,因为我们可以最后处理它们。

很显然要做DP,顺序可以直接从左到右,满足无后效性。需要最后一个书的编号,以便后面使用。当然取出了几本也得压进状态。

为了处理拿出去的书,我们得把书的状态集合给存储下来。因为拿出去的书拿走以后,可能在原书架里面没有了,最后要根据状态放回来。

方程:
\(dp[i][j][sta][l]\)表示\(i\)位置第\(j\)次换书之前的书的状态集合为\(sta\)当前书的集合最后一本为\(l\)


Code:

#include <cstdio>
#include <cstring>
const int N=104;
const int inf=0x3f3f3f3f;
int min(int x,int y){return x<y?x:y;}
int dp[2][N][1<<10][10],n,k,r,a[N];
void DP()
{
    for(int i=1;i<=n;i++)//位置
    {
        memset(dp[i&1],0x3f,sizeof(dp[i&1]));
        for(int j=0;j<=min(k,i);j++)//拿走了几个
            for(int sta=0;sta<(1<<r);sta++)//前面的状态,这个没填
                for(int l=0;l<=r;l++)//末尾书本
                    if(!l||(sta>>l-1)&1)
                    {
                        dp[i&1][j][sta|(1<<a[i]-1)][a[i]]=min(dp[i&1][j][sta|(1<<a[i]-1)][a[i]]
                                                           ,dp[i-1&1][j][sta][l]+(l!=a[i]));//不拿
                        if(j)
                            dp[i&1][j][sta][l]=min(dp[i&1][j][sta][l],dp[i-1&1][j-1][sta][l]);//拿走
                    }
    }
}
int cal(int x)
{
    int cnt=0;
    while(x)
    {
        cnt++;
        x-=x&-x;
    }
    return cnt;
}
int main()
{
    scanf("%d%d",&n,&k);
    int cnt=0;
    while(n&&k)
    {
        int sta=0;
        r=0;cnt++;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",a+i);
            a[i]-=24;
            r=r>a[i]?r:a[i];
            sta|=1<<a[i]-1;
        }
        memset(dp[0],0x3f,sizeof(dp[0]));
        dp[0][0][0][0]=0;
        DP();
        int ans=inf;
        for(int i=1;i<=k;i++)
            for(int j=0;j<(1<<r);j++)
                for(int l=1;l<=r;l++)
                    if(dp[n&1][i][j][l]!=inf)
                        ans=min(ans,dp[n&1][i][j][l]+cal(sta-j));
        printf("Case %d: %d\n\n",cnt,ans);
        scanf("%d%d",&n,&k);
    }
    return 0;
}

2018.7.6

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9275523.html