HDU-3237(状压DP)

题目:click
题意:给定n个书本的高度,若一个区间有连续的高度相同则示为一个混乱度(只有一个也占一个混乱度),告诉你能拿k本书之后放回去,问最小混乱度。

题目中说明了书的高度25-32,加上n和k的大小,可以进行状态压缩dp,
再来观察每一本书,当到第i本书的时候我们可以选择拿或者不拿,将25-32压缩状态,由于每次拿第i本书的时候需要考虑到前一本书的状态,再设置一个最后的书本高度,dp[i][j][s][h]:表示前i本书 拿了j本出来 剩余的书本状态s(0—1<<8) 最后一本书的高度h 的最小混乱度。
进行分析状态转移方程:
当第i本书我选择拿的时候:
我选择不拿第i本书,但是需要确定一下之前最后一本书的高度是否相同。

精妙精妙精妙的状压dp,把所有的书本状态用一个变量表示出来,再用该变量与枚举s进行对比,相互异或可以得到拿走的书,0-1的相互改变表示它拿走书的混乱度变化从这里切入,只需要计算异或的结果1的个数即混乱度。
奥 题目卡内存,注意到只有两层的状态相互转移,改为滚动数组即可。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define MAX_len 50100*4
using namespace std;
typedef long long ll;
int n,k;
int a[110];
int dp[3][110][(1<<8)][10];// 前i个  拿走了j个 剩余的书本的状态s 最后一位的高度temp 的mess MIN
int main()
{
    int yy=1;
    while(~scanf("%d %d",&n,&k)&&(n||k))
    {
        memset(dp,inf,sizeof(dp));
        int i,j,s;
        int hh=0;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i]-=25;
            hh|=(1<<a[i]);
        }
        dp[1][1][0][8]=0;//没有任何书放在桌子上
        dp[1][0][0|(1<<a[1])][a[1]]=1;
        for(i=2;i<=n;i++)
        {
            memset(dp[(i)%2],inf,sizeof(dp[(i)%2]));
            for(j=0;j<=k&&j<i;j++)
            {
                for(s=0;s<(1<<8);s++)
                {
                    for(int temp=0;temp<=8;temp++)
                    {
                        if(dp[(i+1)%2][j][s][temp]==inf)//不存在这种状态
                        continue;
                        if(j<k)//取走当前得这本书
                        {
                            dp[i%2][j+1][s][temp]=min(dp[i%2][j+1][s][temp],dp[(i+1)%2][j][s][temp]);
                        }
                        //不拿
                        if(a[i]==temp)
                        {
                            dp[i%2][j][s][temp]=min(dp[i%2][j][s][temp],dp[(i-1)%2][j][s][temp]);
                        }
                        else
                        dp[i%2][j][s|(1<<a[i])][a[i]]=min(dp[i%2][j][s|(1<<a[i])][a[i]],dp[(i-1)%2][j][s][temp]+1);
                    }
                }
            }
        }
        int ans=n;
        for(j=0;j<=k;j++)
        {
            for(s=0;s<(1<<8);s++)
            {
                for(int temp=0;temp<8;temp++)
                {
                    int cnt=0;
                    int tmp=hh^s;
                    while(tmp)
                    {
                        if(tmp&1)
                            cnt++;
                        tmp>>=1;
                    }
                    ans=min(ans,dp[n%2][j][s][temp]+cnt);
                }
            }
        }
        printf("Case %d: %d\n\n",yy++,ans);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值