• UVALive 4490 Help Bubu


      题目大意:有n本书,高度值域为8,现可以把k本书拿出来再放进去,相邻的、高度相同的书算作一块,最小化块的个数。n=100。

      强烈建议大家不要在做完区间DP后做别的DP题:区间DP是整体考虑,而一般DP是考虑以i为末尾,思路完全不同。

      难点好像就在于设状态,状态设出来就可以大力转移了。

      首先f[i][j],表示到第几本书、用了几次是肯定要设的,但是这样做会缺少很多信息。在转移的时候,我们需要知道最后一位是什么?所以开到f[i][j][k]。

      这个就可以DP了。但是答案怎么统计呢?如果最终答案是把几种高度全部扣出来,答案就要比DP值大,但这是我们状态中不能体现的。

      看到值域这么小,直接设f[i][j][k][l],表示到第i本书、用了j次取书机会、在书架上的书的集合是k,最后一本书是l的最小块数。

      初始化:

    f[i+1][i][bin[h[i+1]]][h[i+1]]=1;
    初始化

      转移方程就是讨论一下就可以出来的东西了。

      1.当前书取出来:

    f[i+1][j+1][k][l]=min(f[i+1][j+1][k][l],f[i][j][k][l]);
    转移1

      2.当前书不取,这时要与最后一位做比较:

    f[i+1][j][k|bin[h[i+1]]][h[i+1]]=min(f[i+1][j][k|bin[h[i+1]]][h[i+1]],f[i][j][k][l]+(h[i+1]!=l));
    转移2

      其中bin表示2的多少次方。

      在统计答案的时候,要这么统计:

    for(int i=0;i<=k;++i)
        for(int j=0;j<bin[8];++j)
          for(int k=0;k<8;++k)
            if(f[n][i][j][k]!=f[0][0][0][0])
              Ans=min(Ans,f[n][i][j][k]+num[U^j]);
    统计答案

      其中num表示二进制下有多少个1。

      完整代码:

    #include    <iostream>
    #include    <cstdio>
    #include    <cstdlib>
    #include    <algorithm>
    #include    <vector>
    #include    <cstring>
    #include    <queue>
    #include    <complex>
    #include    <stack>
    #define LL long long int
    #define dob double
    #define FILE "4490"
    using namespace std;
    
    const int N = 110;
    int n,k,bin[10],h[N],f[N][N][1<<8][9],t,Ans,num[1<<8];
    
    inline int gi(){
      int x=0,res=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
      while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
      return x*res;
    }
    
    inline void Min(int &x,int y){
      if(x>y)x=y;
    }
    
    inline void solve(int U=0){
      for(int i=1;i<=n;++i)U|=bin[h[i]=gi()-25];
      memset(f,127/3,sizeof(f));Ans=f[0][0][0][0];
      f[1][0][bin[h[1]]][h[1]]=1;
      for(int i=1;i<n;++i){
        f[i+1][i][bin[h[i+1]]][h[i+1]]=1;
        for(int j=0;j<=i && j<=k;++j)
          for(int k=0;k<bin[8];++k)
            for(int l=0;l<8;++l)
              if(f[i][j][k][l]!=Ans){
                Min(f[i+1][j+1][k][l],f[i][j][k][l]);
                Min(f[i+1][j][k|bin[h[i+1]]][h[i+1]],f[i][j][k][l]+(h[i+1]!=l));
              }
      }
      for(int i=0;i<=k;++i)
        for(int j=0;j<bin[8];++j)
          for(int k=0;k<8;++k)
            if(f[n][i][j][k]!=f[0][0][0][0])
              Min(Ans,f[n][i][j][k]+num[U^j]);
      printf("Case %d: %d
    
    ",++t,Ans);
    }
    
    int main()
    {
      freopen(FILE".in","r",stdin);
      freopen(FILE".out","w",stdout);
      bin[0]=1;for(int i=1;i<10;++i)bin[i]=bin[i-1]*2;
      for(int i=1;i<bin[8];++i)num[i]=num[i/2]+(i&1);
      while((n=gi()) && (k=gi()))solve();
      fclose(stdin);fclose(stdout);
      return 0;
    }
    Help Bubu
  • 相关阅读:
    快速导出B站收藏单节目列表
    打包Python程序
    PHP应用日志记录
    这几天折腾win10访问deepin共享的历程
    PHP更新本地公网IP到阿里云域名解析,实现DDNS
    PHP.ini 能不能加载子配置文件 ?
    PHP处理表单数据的一个安全回顾(记录教训)
    不用U盘,用一台好电脑给另一个电脑重装windows10
    方正 ignb路由器设置备份(自用笔记)
    Xiuno BBS 4.0 修改时间显示
  • 原文地址:https://www.cnblogs.com/fenghaoran/p/7672813.html
Copyright © 2020-2023  润新知