hiho 8 状态压缩dp

问题描述

具体参见:http://hihocoder.com/contest/hiho8/problem/1

解决方法

由于每个位置是否清扫,取决于该位置之前的M-1个位置是否清扫过,因此状态可以表示为: best(i,p0,p1,p2,...pm1)
p0 表示第i个位置是否清扫,1, 清扫,0, 不清扫。
p1 表示第i-1位置是否清扫,1, 清扫,0, 不清扫。

pm1 表示第i-(m-1)个位置是否清扫,1, 清扫,0, 不清扫。
best(i,p0,p1,p2,...pm1) 表示确定了从i-(m-1) 到i的清扫状态后,可以取得的最大值。
由于状态变量太多,不容易编程实现,状态压缩的含义就是用二进制串表示
p0,p1,p2,...pm1 这些状态变量。
由于二进制串表示后,有些状态是不满足条件的,如果for循环填表,要考虑很多细节的东西,我们可以使用记忆搜索的方法,虽然效率上慢点,但是程序简洁了不少,也容易理解。
最后上代码:

#include <cstdio>
#include<cstring>
#include <algorithm>
using namespace std;
enum {maxn = 1000+4, maxs = (1<<10)+3};
int dp[maxn][maxs];
int w[maxn];
 int n, m, q;
int bitCount(int i)
{
    int num =0;
    while(i){
        if (i & 0x01)
            num++;
        i>>=1;
    }
    return num;
}
int best(int i, int s)
{
    if (dp[i][s] >= 0)
        return dp[i][s];

    int num = bitCount(s);
    if (num >q)
        return dp[i][s] = 0;
    dp[i][s] = max(best(i-1, s>>1), best(i-1, (s>>1) + (1<<(m-1))));
    if (s & 0x01)
    {
        return dp[i][s] += w[i];
    }else{
        return dp[i][s];
    }

}
#define OJ
int main()
{
    #ifndef OJ
    freopen("in.txt", "r", stdin);
    #endif // OJ

    scanf("%d %d %d", &n, &m, &q);
    memset(dp, -1, maxn * maxs*sizeof(int));
    memset(dp, 0, maxs*sizeof(int));
    for (int i=1; i<= n; i++)
        scanf("%d", w+i);
    int res=-1;
    for (int i=0; i<= (1<<m)-1; i++)
    {
        res = max(res, best(n, i));
    }
    printf("%d\n", res);
    return 0;
}

解法二: 从前往后递推:

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

int n, m, q;
enum {maxn =1000+4, maxm2 = 1<<11};
int dp[maxn][maxm2];
int numOne[maxm2];

int getNumOne(int a)
{
    int cnt =0;
    for (; a; a>>=1)
        if (a&0x01)
            cnt++;
    return cnt;
}

int main()
{
    scanf("%d %d %d", &n, &m, &q);
    memset(dp, 0, sizeof(dp));

    for (int i=0; i< 1<<m; i++)
        numOne[i] = getNumOne(i);

    for (int i=0; i< n;i++)
    {
        int v;
        scanf("%d", &v);
        for (int j=0; j< (1<<m); j++)
        {
            if (numOne[j] <= q)
            {
                int k = j>>1;
                dp[i+1][k] = std::max(dp[i+1][k], dp[i][j]); // no chose;
                k = j>>1 | (1<<(m-1));
                if (numOne[k] <=q)
                    dp[i+1][k] = std::max(dp[i+1][k], dp[i][j] + v);
            }
        }

    }

    int res =0;
    for (int i=0; i< (1<<m); i++)
        res = std::max(res, dp[n][i]);
    printf("%d\n", res);

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值