LA4490

此题居然备注是请读者自己思考。。。。。。我是不是太菜了呢? 我是没有思考出来

我本来想如果我把每次拿出i本书的所有状态都存下来。然后在依次往上找是不是算构造来最优子结构?但是此方法太暴力。而且我也不知到该怎么存下来。

虽然我开始确实想到来用状态压缩。但是我不知到怎么保存各个状态后我拿掉某个对于另外一个的个数加减造成的影响。比方说 25 25 26 26 25 25 那么如果状态表示那么拿的状态是11000000 但是如果我左边25放一个到右边25那么状态是没变化的。可能是我没想到把。。。后来呢我还是选择来搜题解。。。想不到现场赛题目这么难啊这么难四维的状态然后还加上状态压缩和滚动数组。。。弱爆了我。

以下题节我参照了几个博客的:

http://blog.csdn.net/woshi250hua/article/details/7743328

http://blog.csdn.net/acm_ted/article/details/7835602

http://hi.baidu.com/huicpc0328/item/20d4c516949e1ffc9c778ac8

说下正文:

首先:这个大家都想到把,由于书的高度介于25和32之间,可以把书的高度减去25变成介于0和7之间,这个数字就很适合状态压缩了。

然后:思考的时候就尽量往高度进行压缩。每本书有两种选择,一种留下,一种抽走,留下的书和抽走的书可以用两个状态表示,如果抽出去的书不在留下的书里面,那么最后就要增加一段。每本书似乎只和留下来的最后一本书的状态有关,如果书的高度和留下的最后一本书的高度相同,那么可以直接合并进去而不必计算高度,如果不相同则要增加段数

这里我觉得为什么要和最后一本书有关呢? 其实在状态后设置一维存状态高度,那么相对与每个状态的各个高度放最后我门都有遍历到。因为我从最开始什么书都没有然后每次往里放一本或抽一本,当放一本进去的时候比较的是当前书架上已经存在的书,所以如果不存在那么其实就只要和最后一本有关啦(个人见解,如有错误还望指正,感激不尽)

接下来:那我们要怎么表示抽走的状态呢?压缩成一个数字吗?不行,还必须和k扯上关系,也就是说必须有一维表示数量,而抽走书的种类可以用总状态减去留下来的状态来表示。这样就可以用dp[i][j][k][s]来进行状态转移,dp[i][j][k][s]表示到第i本书时取走j本书留下来的书状态为k最后一本书高度为s。状态转移见代码。复杂度O(n*k*(1<<8)*8)。

  1. dp[i][j][k][s],前i个位置,花费j次操作,k为为未拿出书的集合,最后一本书的高度为s  
  2. 不取走第i位  1.s==tall[i]  dp[i][j][k][s]=min(dp[i][j][k][s],dp[i-1][j][k][s]  
  3.                     2.s!=tall[i]   dp[i][j][k|(1<<tall[i])][tall[i]]=min(dp[i][j][k|(1<<tall[i])][tall[i]],dp[i-1][j][k][s]+1)  
  4. 取走第i位                       dp[i][j+1][k][s]=min(dp[i][j+1][k][s],dp[i-1][j][k][s]) 

这里我还有最后一个地方不懂,就是为什么最后要统计没有输入的书的状态个数呢??即我最后一行我代码统计min的时候要抑或的地方,我觉得为什么不直接是begin呢?但是我试着交代码错了。!!求教大神提点



#include <iostream>
#include <cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF= 0x3f3f3f3f;
int tall[105],num[1<<8];
int dp[2][110][1<<8][10];
void init()
{
    memset(num,0,sizeof(num));
    //求每个状态中书的数目
    for(int i=0;i<(1<<8);++i)
    for(int j=0;j<8;++j)
    if(i&(1<<j))num[i]++;
}
void preinit(int K,int cur)
{
    for(int j=0;j<=K;++j)
    for(int k=0;k<(1<<8);++k)
    for(int s=0;s<=8;++s)
        dp[cur][j][k][s]=INF;
}
int main()
{
    init();
    int n,m,begin,cas=1;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0)return 0;
        //初始高度状态
        begin=0;
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&tall[i]);
            tall[i]-=25;
            //统计高度个数
            begin |=(1<<tall[i]);
        }
        for(int i=0;i<=m;++i)//取走的次数
        for(int j=0;j<(1<<8);++j)//高度状态
        for(int k=0;k<=8;++k)//最后的书的高度
        dp[0][i][j][k]=INF;
        dp[0][0][0][8]=0;//起始点
        for(int i=1;i<=n;++i)
        {
            int cur=1&i;
            int pre=1-cur;
            preinit(m,cur);
            for(int j=0;j<i&&j<=m;++j)
            for(int k=0;k<(1<<8);++k)
            for(int s=0;s<=8;++s)//因为此题我对于每个状态都设置了一维来存各个状态下的最后一个数,所以所有情况都可以遍历到
            if(dp[pre][j][k][s]!=INF)
            {
                int curstk=k|(1<<tall[i]);//当前状态(放入i高度的书)
                //取走第i位,且还可以取走
                //如果还可以取书或者放书,那么我就要比较对于当前状态和前一个状态哪个下,如果在放一本书后哪个的情况更小
                if(j<m)
                dp[cur][j+1][k][s]=min(dp[cur][j+1][k][s],dp[pre][j][k][s]);
                //取走第 i 位的书且高度与前一个相同(在留下的书里面)
                if(s==tall[i])
                dp[cur][j][k][s]=min(dp[cur][j][k][s],dp[pre][j][k][s]);
                //不取走第 i 位且高度与前一个不同
                else
                dp[cur][j][curstk][tall[i]]=min(dp[cur][j][curstk][tall[i]],dp[pre][j][k][s]+1);
            }
        }
        int ans=INF;
        int cur=n&1;
        for(int i=0;i<=m;++i)
        for(int j=0;j<(1<<8);++j)
        for(int k=0;k<8;++k)
        if(dp[n&1][i][j][k]!=INF)
        {
            int temp=j^begin;//不在初始状态且拿出的书(高度)
            ans=min(ans,dp[cur][i][j][k]+num[temp]);
          //ans=min(ans,dp[cur][i][begin][k]);
        }
        printf("Case %d: %d\n\n",cas++,ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值