欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HDU-3237-Help Bubu

程序员文章站 2022-07-16 09:35:41
...

ACM模版

描述

HDU-3237-Help Bubu

题解

状压 DP

dp[i][j][k][s] 表示为前 i 本书拿走 j 本剩下的书状态为 k 最后一本书的高度是 s 的最少类数。这里的状态指的是剩下的书所有的高度,而不是剩下的书的序列状态,为什么要这样设置呢?因为我们设置的抽出的书并没有直接考虑如何插入,而是放到最后统一插入,如果抽出的书中有高度 x,而剩下的书也有高度 x 自然是不需要产生更多类的,否则就需要产生新的类。所以最后遍历一遍 dp[cur],判断不同状态 k 所对应的被抽书状态与 k 的差集,这个差集就是需要产生的新的类。

神乎其技的状压 DP,注意边界问题,注意滚动数组。

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>

using namespace std;

const int MAXB = 8;
const int MAXN = 110;
const int MAXM = 1 << MAXB;
const int LIMIT = 25;
const int INF = 0x3f3f3f3f;

int n, m, ans;
int book[MAXN];
int cnt[MAXM];
int state;
int dp[2][MAXN][MAXM][MAXB + 1];

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

int main()
{
    init();

    int ce = 0;

    while (~scanf("%d%d", &n, &m) && n + m)
    {
        int mx = state = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", book + i);

            book[i] -= LIMIT;
            if (book[i] > mx)
            {
                mx = book[i];
            }
            state |= (1 << book[i]);
        }

        ++mx;
        int tot = (1 << mx);
        memset(dp[1], 0x3f, sizeof(dp[1]));
        dp[1][0][1 << book[1]][book[1]] = 1;
        dp[1][1][0][mx] = 0;
        for (int i = 2; i <= n; i++)
        {
            int cur = i & 1;
            int pre = 1 - cur;
            memset(dp[cur], 0x3f, sizeof(dp[cur]));

            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 tmp = k | (1 << book[i]);
                        if (j < m)
                        {
                            dp[cur][j + 1][k][s] = min(dp[cur][j + 1][k][s], dp[pre][j][k][s]);    //  取最后一本
                        }

                        if (s == book[i])
                        {
                            dp[cur][j][k][s] = min(dp[cur][j][k][s], dp[pre][j][k][s]);
                        }
                        else
                        {
                            dp[cur][j][tmp][book[i]] = min(dp[cur][j][tmp][book[i]],
                                                           dp[pre][j][k][s] + 1);
                        }
                    }
                }
            }
        }

        int cur = n & 1;
        int ans = n, st;
        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)
                    {
                        st = state ^ k;        //  抽走的就是额外的类
                        ans = min(ans, cnt[st] + dp[cur][j][k][s]);
                    }
                }
            }
        }

        printf("Case %d: %d\n\n", ++ce, ans);
    }

    return 0;
}