F题题解

思路

线性dp
(1)状态表示: 
 f[i][0]:长度为i且第i个珠子为蓝色时的最大价值,若第i个珠子不可以为蓝色,则值为负无穷
 f[i][1]:长度为i且第i个珠子为红色时的最大价值,若第i个珠子不可以为红色,则值为负无穷
 w[i][0]:第i个位置 蓝色珠子的最大价值,若无蓝色珠子则取负无穷代表取不到
 w[i][1]: 第i个位置 红色珠子的最大价值,若无红色珠子则取负无穷代表取不到

(2)集合划分
case 1:第一个珠子取红色时 
初始化:    f[1][1]=w[1][1] , f[1][0]=负无穷  
状态计算:   f[i][0]=max(f[i-1][1],f[i-1][0])+w[i][0] 
           f[i][1]=f[i-1][0]+w[i][1]

 => ans1 = f[n][0]

case 2:第一个珠子取蓝色时
初始化:   f[1][0]=w[1][0] , f[1][1]=负无穷
状态计算: f[i][0]=max(f[i-1][1],f[i-1][0])+w[i][0] 
          f[i][1]=f[i-1][0]+w[i][1]    

 => ans2 = max(f[n][0],f[n][1])

最后的结果是 max(ans1,ans2)

ps: 在进行分类讨论第1个珠子为红色还是蓝色 之前,我们需要进行特判:
   若 必存在两个红色珠子相邻的情况,输出 -1
   若 只有1个珠子即n=1时,输出 max(w[1][0],w[1][1])
   在特判之后 再进行分类讨论......

综上,Code 如下

Code

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

using namespace std;

typedef long long ll;

const int N=1e6+10;
const ll INF=-1e18; 

ll f[N][2];
ll w[N][2];
ll val[N];     // 珠子的价值
bool st[N][2]; // 用于看第i个位置是否有蓝色珠子和红色珠子
int n,m;

int main(){
    int T;
    scanf("%d",&T);

    while(T--){ 
        // 此处for循环不可以用memset代替,会超时
       for(int i=1;i<=n;i++) {
         f[i][1]=f[i][0]=0;
         w[i][1]=w[i][0]=-1e18;   
         st[i][1]=st[i][0]=false;
        }
        scanf("%d %d",&n,&m);

        int t=0;
        for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)  scanf("%lld",&val[++t]); 

        t=0;
        int flag=0;
        for(int i=1;i<=n;i++){
           for(int j=1;j<=m;j++)  {
               t++;
               int x;
               scanf("%d",&x);
               if(x)   w[i][1]=max(w[i][1],val[t]),st[i][1]=true; //第i个位置的红色最大值      
               else w[i][0]=max(w[i][0],val[t]),st[i][0]=true;  //第i个位置的蓝色最大值
           }  
           if(i>=2&&st[i][0]==false&&st[i-1][0]==false) flag=1;
        }
        if(st[1][0]==false&&st[n][0]==false) flag=1;

        if(n==1)     printf("%lld\n",max(w[1][0],w[1][1]));
        else if(flag) printf("-1\n");
        else {
             ll res=0;
            //第一个取红色时
            f[1][1]=w[1][1];
            f[1][0]=INF;    // 不可以等于0,必须极小代表取不到  
            for(int i=2;i<=n;i++){
               f[i][1]=f[i-1][0]+w[i][1];
               f[i][0]=max(f[i-1][1],f[i-1][0])+w[i][0];
             }
             res=f[n][0];

            // 第一个取蓝色时
            for(int i=1;i<=n;i++)  f[i][1]=f[i][0]=0;
            f[1][1]=INF;   // 不可以等于0,必须极小 
            f[1][0]=w[1][0];
            for(int i=2;i<=n;i++){
               f[i][1]=f[i-1][0]+w[i][1];
               f[i][0]=max(f[i-1][1],f[i-1][0])+w[i][0];
             }

            res=max(res,max(f[n][1],f[n][0]));
            printf("%lld\n",res);
        }   
    }
    return 0;
}