hihoCoder 1044 状态压缩·一 (状压dp)

题意:

一个n长的序列,每个位置都有一个值w,每次连续的M个最多只能取Q个,问如何取使得获得最多的价值。

题解:

这题类似背包,对应每连续的序列进行转移到下个连续的序列,对于新加进来的位置有两个决策,一是取,二是不取。例如 1010,转到下个序列为0100,最后一位是新加进来的考虑取或不取的状态。于是可以这样设置dp[i][s],表示以i为结尾的M个连续序列选取的情况状态为s,获得的最大值。

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define B(x) (1<<(x))
using namespace std;
typedef long long ll;
void cmax(int& a,int b){ if(b>a)a=b; }
void cmin(int& a,int b){ if(b<a)a=b; }
void cmax(ll& a,ll b){ if(b>a)a=b; }
void cmin(ll& a,ll b){ if(b<a)a=b; }
void add(int& a,int b,int mod){ a=(a+b)%mod; }
void add(ll& a,ll b,ll mod){ a=(a+b)%mod; }
const int oo=0x3f3f3f3f;
const ll OO=0x3f3f3f3f3f3f3f3f;
const ll MOD=1000000007;
const int maxn=105;
const int maxm=10000005;
int dp[1005][B(10)+5];
int w[1005],ok[B(10)+5];

int main(){
    //freopen("E:\\read.txt","r",stdin);
    int N,M,Q,ans;
    while(scanf("%d%d%d",&N,&M,&Q)!=EOF){
        memset(dp,-1,sizeof dp);
        memset(ok,0,sizeof ok);
        for(int i=1;i<=N;i++)scanf("%d",&w[i]);
        if(N<M) M=N;
        int full=B(M)-1;
        for(int s=0;s<=full;s++){
            int cnt=0,sum=0;
            for(int i=0;i<M;i++)if(s&B(i)){
                cnt++;
                sum+=w[M-i];
            }
            if(cnt<=Q){
                dp[M][s]=sum;
                ok[s]=1;
            }
        }
        for(int i=M+1;i<=N;i++){
            for(int s=0;s<=full;s++)if(dp[i-1][s]!=-1){
                int s1=((s<<1)&full);
                int s2=((s<<1|1)&full);

                if(ok[s1])
                cmax(dp[i][s1],dp[i-1][s]);
                if(ok[s2])
                cmax(dp[i][s2],dp[i-1][s]+w[i]);
            }
        }
        ans=0;
        for(int s=0;s<=full;s++)
            cmax(ans,dp[N][s]);
        printf("%d\n",ans);
    }
    return 0;
}

/*
3 5 4
1 10 100
3 5 2
1 10 100

*/




你可能感兴趣的:(hihoCoder 1044 状态压缩·一 (状压dp))