[UVALive 4490] Help Bubu

图片加载可能有点慢,请跳过题面先看题解,谢谢
1196604-20171015152622184-1027858437.png
1196604-20171015152626121-899906610.png

首先我们很容易注意到一件事情:\(h\) \(\epsilon\) {\(25,26,27,28,29,30,31,32\)},数一下,\(8\)个数,上状压
设状态 \(f[i][j][s][g]\) 为,处理到第 \(i\) 本书,抽掉了$ j$ 本,剩余书的状态为 \(s\) (一个二进制数),最后一本放 \(g\) 的最小步数
我们分三种情况转移:

  1. \(i\) 本和第 \(i-1\) 本相同,则有:\(f[i][j][s][g]=min(f[i-1][j][s][g])\)
  2. \(i\) 本和第 \(i-1\) 本不同,且抽掉第 \(i\) 本,则有:\(f[i][j+1][s][g]=min(f[i-1][j][s][g])\)
  3. \(i\) 本和第 \(i-1\) 本不同,且不抽掉第 \(i\) 本,则有:\(f[i][j][s|(1<<(a[i]-25))][a[i]-25]=min(f[i-1][j][s][g]+1)\)

其中,\((1<<g)\leq s\)\(s\) \(\epsilon\) \(S\)\(S\) 为当前所有出现过的书的状态,\(s\) 可以通过枚举 \(S\) 的子集得到
当然,第一维可以滚一维,节省空间,也可以节省 \(memset\) 的时间
最后答案就是 \(min(f[n][j][s][g]+calc(S\) ^ \(s))\)\(calc(S\) ^ \(s)\)\(S\) ^ \(s\)\(1\) 的个数

//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define inf (0x3f3f3f3f)
#define il inline
#define RG register
using namespace std;
il int gi(){ RG int x=0,q=1; RG char ch=getchar(); while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
  if( ch=='-' ) q=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; }

int S,x,t;
int n,k,f[110][110][1<<8][10];

il void init(){ S=0; memset(f,0x3f,sizeof(f)); }

il int min(int &a,int b){return a=a<b?a:b;}
il int calc(int c){ return (!c)?0:calc(c/2)+(c%2); }

il void work(){
   for(int i=1;i<=n;i++){
      scanf("%d",&x); x-=25;
      f[i][i-1][1<<x][x]=1;
      for(int j=0;j<=min(i-1,k);j++)
         for(int s=S;s;s=(s-1)&S)
            for(int g=0;(1<<g)<=s;g++)
               if(f[i-1][j][s][g]!=inf){
                  int y=f[i-1][j][s][g];
                  if(g==x) min(f[i][j][s][g],y);
                  else{
                     min(f[i][j+1][s][g],y);
                     min(f[i][j][s|(1<<x)][x],y+1);
                  }
               }
      S|=(1<<x);
   }
   int ans=inf;
   for(int j=0;j<=k;j++)
      for(int s=S;s;s=(s-1)&S)
         for(int g=0;(1<<g)<=s;g++)
            if(f[n][j][s][g]!=inf)
               ans=min(ans,f[n][j][s][g]+calc(S^s));
   printf("Case %d: %d\n\n",++t,ans);
}

int main(){ while(scanf("%d%d",&n,&k)&&n){ init(); work(); } return 0; }

转载于:https://www.cnblogs.com/Hero-of-someone/p/7671242.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值