K题题解

方法:状态压缩DP——(视频)https://www.bilibili.com/video/BV1Uk4y117z1/?spm_id_from=333.337.search-card.all.click&vd_source=c13d187ae699de8a29dd35ec302e586f

dp的思想:dp一般用于解决多阶段决策问题,即每个阶段都要做一个决策,全部的决策是一个决策序列,要你求一个最好的决策序列使得这个问题有最优解.dp的每个决策之间有联系;

思路:盖子有三个状态左移右移和不动,状态压缩就是用数字来表示这些状态即不动0,左移-1,右移1; 接下来怎么写呢?

1.先写转移方程,即思考这一步等于这上一步加这步的最优解。

2.要保证相邻的盖子位置合法,这里就是 1.pos[i]+j>pos[i-1]+k

3.要知道 dp 的结束条件,这里就是枚举小于盖子数量 cnt 时; 4.要初始化 dp 数组,因为我们是由前面推到后面的。

dp[i][j] 表示 0--i 的位置为j的状态时的最好决策,ji 时的盖子状态,ki-1 时的盖子状态;

第一行代码是初始化**dp[1]**的三种状态

**pos[]**是盖子的初始位置,a[] 是盒子的值,k-1 ,是因为左移,位置变化所以是 -1--1

所以最后一步需要得到三个状态的最大值也就是最好决策

(ps:max函数的使用注意事项 max({a,b,c})是将三个数字放在一个数组里用max比较 状态压缩可能会用到的函数:std库里的函数__builtin_popcount(),直接返回十进制数字的二进制表示中的1的数字。)

#include<bits/stdc++.h>
const int N=1e6+10;
typedef long long ll;
ll a[N],b[N],pos[N],dp[N][3];//定义dp数组-1左移,0不动,1右移
int main(){
    ll n,cnt=0;
    std::cin>>n;
    for(int i=1;i<=n;i++){
        std::cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        std::cin>>b[i];
        if(b[i]) pos[++cnt]=i;//把有盖子的位置存下来
    }
    if(!cnt){
        std::cout<<"0";
        return 0;//一个盖子也没有,直接为0;
    }
    if(pos[1]>1) dp[1][-1]=a[pos[1]-1];//第一个盖子可以左移
    dp[1][0]=a[pos[1]];
    if(pos[1]<n) dp[1][1]=a[pos[1]+1];//可往右移动一个
    for(int i=1;i<=cnt;i++){
        for(int j=-1;j<=1;j++){//j是当前状态开始枚举上一个盖子的状态k
            for(int k=-1;k<=1;k++){
                if(pos[i]+j>pos[i-1]+k&&(pos[i]+j)<=n){//合法的时候
                    dp[i][j]=std::max(dp[i][j],dp[i-1][k]+a[pos[i]+j]);
                }
            }
        }
    }
    std::cout<<std::max({dp[cnt][-1],dp[cnt][0],dp[cnt][1]});//max({a,b,c})是将三个数字放在一个数组里用max比较
    return 0;
}