2023.9.9CSP-J模拟赛

比赛网址
我太菜了。。。。。。

T1

水题一道,跳过

T2

简单分讨,刚开始写的太复杂了。后来才发现自己是个**。

T3

给定一个序列,你可以删除一些数,求最终所有剩下的数的和的最大值,满足剩下的每相邻的两个数的和都是质数。

感觉可以二分,但是貌似没啥用。

正解应该是 DP。

两种 dp 思路,其实本质是一样的,但是反着考虑好像不能滚动数组。

  1. \(f_{i,0/1,k}\) 表示考虑前 \(i\) 个数,删掉/保留第 \(i\) 个数,最后一个数是 \(k\) 的剩余数的和的最大值。
    状态转移就有

\[f_{i,1,a_i}=\max(f_{i-1,1,a_i},f_{i-1,0,k}+a_i)\\ f_{i,0,a_i}=\max(f_{i-1,0,a_i},f_{i,1,a_i}) \]

答案为

\[\max\{f_{n,1,a_i}\} \]

  1. \(f_{i,0/1,k}\) 表示考虑前 \(i\) 个数,删掉/保留第 \(i\) 个数,最后一个数是 \(k\) 的删掉数的和的最小值。
    初始状态为

\[f_{0,0,0}=f_{0,1,0}=1 \]

状态转移就有

\[f_{i,0,k}=\min(f_{i-1,0,k,f_{i-1,1,k}})+a_i\\ f_{i,1,a_i}=\min\{\min(f_{i-1,1,k},f_{i-1,0,k})\} \]

答案为

\[sum-\min(\min\{f_{n,0,i}\},f_{n,1,a_n})+1 \]

T4

给定一个序列,求不小于给定长度的子区间的中位数的最大值。

二分答案,每次 \(O(n)\) check。

具体来说,对于每个小于中位数的值,我们记它的代价为 \(-1\),大于中位数的值的代价为 \(1\),判断是否有满足要求的子区间代价的和不小于 \(0\) 即可。

具体实现还是很 trick 性的,在于如何以 \(O(n)\) 的时间复杂度求子区间代价和的最值。还是看看代码吧。

点击查看代码
bool check(int x)
{
    for(int i=1;i<=n;i++)
        if(a[i]<x)sum[i]=sum[i-1]-1;//记录前缀和
        else sum[i]=sum[i-1]+1;
    int ans=0;
    for(int i=m;i<=n;i++)
    {
        ans=min(ans,sum[i-m]);//一边记录最小前缀,一边判断区间是否符合标准,相当于一个前缀最值操作,很精妙
        if(sum[i]>=ans)return 1;
    }
    return 0;
}

考场上没想到二分,实际上仔细想想,还是很精妙的。实际上中位数在序列中刚好也是中间的位置,这和二分的特点也是很像的。如果换成众数,实际上也就没有单调性了,也根本用不到二分

posted @ 2023-09-11 19:40  week_end  阅读(21)  评论(0)    收藏  举报