C.Red Walking on Grid

数组定义: —— 在 列这个位置的走过的R的个数

状态方程:(如果同行能走)

(如果同列能走)(注意!!在两行进行这个决策时,后面那个 应该放的是没有经过方程 的原始值)

思路:我们从左往右(外循环),从上往下(内循环)遍历,就能避免走过这个格子又经过的情况。如果同行能走(两个格子上都是"R"),则先进行方程 ,因为我们实行贪心策略能走则走。如果这一列能走,则我们取MAX,看是”从左边走过来的值大“还是”从右边走过来的值大“,我们取原始值进行比较就是因为不能在这一行里往返走。

 void solve() {
    //code here
    int n;cin >> n;
   
    string str[2];
    cin >> str[0] >> str[1];
    str[0] = 'W' + str[0] + 'W';			//为了方便防止越界
    str[1] = 'W' + str[1] + 'W';
    n += 2;
 
    vector<vector<int>>dp(2,vector<int>(n));
 
    for(int i = 1;i < n - 1;++i){
        if(str[0][i] == 'R')dp[0][i] = dp[0][i - 1] + 1;
        if(str[1][i] == 'R')dp[1][i] = dp[1][i - 1] + 1;
        if(str[0][i] == str[1][i] && str[0][i] == 'R'){
            int t1 = dp[0][i],t2 = dp[1][i];
            dp[0][i] = max(dp[0][i],t2 + 1);
            dp[1][i] = max(dp[1][i],t1 + 1);
        }
    }
  
    int ans = 0;
    for(int i = 1;i < n - 1;++i){
        ans = max({ans,dp[0][i] - 1,dp[1][i] - 1});
    }
    cout << ans << endl;
}

I.Red Playing Cards

数组定义: —— 合并完 位置这个值所对应的区间),所产生的最大值。

状态方程:详情看代码。

思路:先合并小区间,再合并大区间。因为大区间里可能会包含小区间,先合并小区间时有助于我们在合并大区间时进行决策——到底是合并掉这个区间直接取合并后的值大(),还是把这个区间看作纯填坑的数组不合并所拿的值大。对于两个区间交错的问题,我们先分别把这两个区间合并的值搞出来放那(放里),再在合并"包含这两个小区间"的大区间时进行决策取不取。

bool cmp(PII &a,PII &b){
    return a.y - a.x < b.y - b.x;      //长度比较
}

void solve() {
    //code here
    int n;cin >> n;
    int nn = 2 * n;
    vector<int>arr(nn + 2);         //存原始数据数组
    vector<PII>pos(n + 1);          //1-n数字对的左位置和右位置


    for(int i = 1;i <= nn;++i){        //初始数据处理
       cin >> arr[i];
       if(pos[arr[i]].x)pos[arr[i]].y = i;
       else pos[arr[i]].x = i;
    }

    vector<PII>up(n + 1);           //用于复制pos数组
    pos.push_back({0,nn+1});     //为了方便,最后遍历整个数组时而加入。与for(0~nn+1)没有本质区别

    up = pos;
    sort(up.begin(),up.end(),cmp);
    //用于复制pos数组,并按照区间长度从小到大排序
    //目的是为了让小区间先进行合并,大区间后合并,方便大区间包含“整个”小区间时,直接取max决策这个小区间是合并了贡献大还是不合并贡献大
    //如不懂可以先放着看下面

    vector<int>tmp(nn + 2);         //用于每个区间遍历时存值
    vector<int>dp(nn + 2);          //存每个区间遍历完后的值 dp[y] 为 区间[x,y]整合后的最大值(注意:这个值仅存于dp[y]
                                        //目的是方便下面那行max决策时取值

    for(auto [l,r] : up){       //相当于int l = up[i].x ,int r = up[i].y;
        tmp[l] = arr[l];                          //tmp[i] 遍历这个区间时暂时存合并这个最优值(合并好(如果能合并更小的区间)还是不合并好)
        for(int i = l + 1;i <= r;++i){
            tmp[i] = tmp[i - 1] + arr[l];         //不考虑其他,只是遍历

            int left = pos[arr[i]].x;             //假设这个位置存的值是右值   去存当前位置的这个值的左值

            if(left != i && left > l){            //如果当前确实是左值 且在这个大区间里  我就取max进行决策(到底是”合并这个区间值大“还是”不合并大“)
                tmp[i] = max(tmp[i],tmp[left - 1] + dp[i]);  //重点!  dp[i]就是合并这个小区间所获得的值(被合并的值都在dp[i]里),因为被取,所以左边剩下的tmp[left-1]
            }
        }
        dp[r] = tmp[r] ;                          //遍历完后 将合并这个区间的最优解放在dp[r]里
    }

    cout << dp[nn + 1] << endl;                   //输出整个区间的最优解
}