[hihocoder1044]状态压缩一

问题简介

详情请参考 hihocoder官网。从N个数字选取任意个数字使得数字和最大,需要满足连续M个数字中不选择超过Q个。

算法详解

这个问题可以利用动态规划求解,需要按数字枚举,且对于前M个是否选取的状态展开。时间复杂度为O(N*2^M),因为M不大于10,所以O(2^M)=O(1024)=O(1)。
具体的对于第i个数字,存储i-M+1到i这M个数字的2^M种选取情况下,从前i个数字中进行满足条件的选取可以得到的最大值。
问题在于状态是动态且不定的,所以需要对状态进行压缩。具体的,用一个数字即可表示所有的状态,从个位起第k个bit表示第i-k+1个数字是否选取,选取则为1,否则为0.
这样得到的转移方程即为
  • f[i][j] = 0 if cnt[j] > Q
  • f[i][j] = max(f[i-1][j>>1+1<<(M-1)],f[i-1][j>>1])+weight[j]; if j&1==1
  • f[i][j] = max(f[i-1][j>>1+1<<(M-1)],f[i-1][j>>1]); if j&1==0
其中cnt[j]表示j中有多少个1。

代码

#include <iostream>

using namespace std;

void build_cnt(int *cnt,int m){
    int len = 1<<m;
    for (int i = 0;i < len;++i){
        cnt[i] = 0;
        for (int j = 0;j < m;++j)
            cnt[i] += ((i>>j)&1);
    }
}

inline int mmax(int a,int b){
    return (a>b)?a:b;
}

int main()
{
    int n,m,q;
    int weight[1002] = {0};

    cin >> n >> m >> q;
    for (int i = 0;i < n;++i)
        cin >> weight[i];

    int maxw[2][2050] = {0};
    int cnt[2050];
    bool flag = true;

    int len = 1<<(m);
    build_cnt(cnt,m);

//    cout << "cnt: ";
//    for (int i = 0;i < len;++i)
//        cout << cnt[i] << ' ';
//    cout << endl;

    for (int i = 0;i < n;++i){
        for (int j = 0;j < len;++j){
            if (cnt[j] > q)
                maxw[flag][j] = 0;
            else {
                if (j&1){
                    maxw[flag][j] = mmax(maxw[!flag][(j>>1)+(1<<(m-1))],maxw[!flag][(j>>1)])+weight[i];
                }
                else {
                    maxw[flag][j] = mmax(maxw[!flag][(j>>1)+(1<<(m-1))],maxw[!flag][(j>>1)]);
                }
            }
        }
//
//        cout << i << ": ";
//        for (int j = 0;j < len;++j)
//            cout << maxw[flag][j] << ' ';
//        cout << endl;
        flag = !flag;
    }

    int result = 0;
    for (int i = 0;i < len;++i){
        if (result < maxw[!flag][i])
            result = maxw[!flag][i];
    }

    cout << result << endl;

    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值