HDU - 3237 Help Bubu (好题)

本文探讨了在有限拿书次数下,如何从书架上选取书籍以减少类别的数量,详细介绍了动态规划方法和状态转移方程的应用。

题意:书架上有n本书,连续的相同高度的归为一类,现在你最多可以拿走m本书然后可以放在任何位置,求最少的类

思路:看了别人的觉得学到了很多,首先dp[i][j][k][s]表示前i本书拿走j本剩下的书的状态是k最后一本书的高度是s的最少类数,其实当我们在不超过m次的拿书的过程中,如果能让类数达到最少的话,且拿走的书也固定了类数,那么就能求出最小了,觉得第4维的设定很精彩啊,一下子就为状态转移方程提供了思路,每次的书我们有拿与不拿的选择,那么当不拿走的时候如果这本书与目前的最后一本不一样的话就+1,否则不用,同时不断的滚动数组求出最小值,显然后三维就可以解释状态了,缺的是当前和上一步,又加了一维,

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 110;
const int INF = 0x3f3f3f3f;

int n,m,ht[MAXN],ans,one[1<<8];
int state,dp[2][MAXN][1<<8][10];

void init(){
    for (int i = 0; i < (1<<8); i++){
        for (int j = 0; j < 8; j++)
            if (i & (1<<j))
                one[i]++;
    }
}

int main(){
    int cas = 0;
    init();
    while (scanf("%d%d",&n,&m) != EOF && n+m){
        int mx = state = 0;
        for (int i = 1; i <= n; i++){
            scanf("%d",&ht[i]);
            ht[i] -= 25;
            if (ht[i] > mx)
                mx = ht[i];
            state |= (1<<ht[i]);
        }
        ++mx;
        int tot = (1<<mx);
        for (int j = 0; j <= m; j++)
            for (int k = 0; k < tot; k++)
                for (int s = 0; s <= mx; s++)
                    dp[1][j][k][s] = INF;
        dp[1][0][1<<ht[1]][ht[1]] = 1;
        dp[1][1][0][mx] = 0;
        for (int i = 2; i <= n; i++){
            int cur = i & 1;
            int pre = 1 - cur;
            for (int j = 0; j <= m; j++)
                for (int k = 0; k < tot; k++)
                    for (int s = 0; s <= mx; s++)
                        dp[cur][j][k][s] = INF;
            for (int j = 0; j <= m && j < i; j++)
                for (int k = 0; k < tot; k++)
                    for (int s = 0; s <= mx; s++){
                        if (dp[pre][j][k][s] == INF)
                            continue;
                        int stk = k | (1<<ht[i]);
                        if (j < m)
                            dp[cur][j+1][k][s] = min(dp[cur][j+1][k][s],dp[pre][j][k][s]);
                        if (s == ht[i])
                            dp[cur][j][k][s] = min(dp[cur][j][k][s],dp[pre][j][k][s]);
                        else dp[cur][j][stk][ht[i]] = min(dp[cur][j][stk][ht[i]],dp[pre][j][k][s]+1);
                    }
        }
        int cur = n&1;
        int ans = n;
        for (int j = 0; j <= m; j++)
            for (int k = 0; k < tot; ++k)
                for (int s = 0; s < mx; s++)
                    if (dp[cur][j][k][s] != INF){
                        int st =  state ^ k; //抽走的就是额外的类
                        ans = min(ans,one[st]+dp[cur][j][k][s]);
                    }
        printf("Case %d: %d\n\n",++cas,ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值