跳转至

C. Median Partition

时间限制:2 秒
内存限制:256 MB

问题描述

你有一个长度为奇数 n 的数组 a,由正整数组成。你需要将序列划分为若干个子数组,每个子数组的长度为奇数且中位数† 相同。你的任务是找出子数组的最大数量*。

更正式地说,你需要找到一个长度为 p+1 的严格递增序列 k,满足 k_1 = 1k_{p+1} = n+1,使得对于每个 1 \le i \le p,序列 [a_{k_i}, a_{k_i+1}, \dots, a_{k_{i+1}-1}] 的中位数全相等。同时 k_ik_{i+1} 的奇偶性应不同。你的任务是找出最大的可能的 p 值。

  • 数组 b 是数组 a 的子数组,如果 b 可以通过删除 a 开头若干个(可能为零或全部)元素以及末尾若干个(可能为零或全部)元素得到。

† 奇数长度 x 的数组的中位数是数组按非递减顺序排序后的第 \lceil \frac{x}{2} \rceil 个元素。

输入格式

每个测试包含多个测试用例。第一行包含整数 t1 \le t \le 1000),表示测试用例的数量。接下来是每个测试用例的描述。

每个测试用例的第一行包含一个整数 n1 \le n < 5000n 为奇数)——数组 a 的长度。

第二行包含 n 个整数 a_1, a_2, \dots, a_n1 \le a_i \le 10^9)——数组 a 的元素。

保证所有测试用例的 n^2 之和不超过 5000^2

输出格式

对于每个测试用例,输出一个整数——子数组的最大数量。

样例输入

10
5
3 3 2 4 3
7
9 5 7 7 4 7 7
9
1 1 1 1 1 1 1 1 1
1
5
3
1 2 3
3
2 2 2
5
1 2 3 4 5
5
2 1 3 2 2
7
2 2 1 2 3 2 2
9
2 1 2 3 2 1 2 3 2

样例输出

3
3
9
1
1
3
1
3
5
5

样例说明

第一个测试用例:最优划分方式为 [3,3,2,4,3]。——(注:原文说明似乎不完整,根据输出推断划分成了 [3,3][2,4,3] 等,但需实际验证。此处按题意理解。)

第二个测试用例:最优划分方式为 [9,5,7][7][4,7,7]

第三个测试用例:由于所有元素相同,可以将每个元素单独划分为一个子数组。

AC 代码

  • 有意思的DP 题目
  • 首先观察到中位数就是全局中位数
  • 其次我们就可以开始DP 了
  • 这里面有一个很妙的技巧,我又开始一直想着怎么记录位置,因为比如正着来一会儿就会遇见一次big=less ,那我怎么记录这些位置呢,所以正着是有点复杂的
  • 所以我们直接倒着循环,解决了从哪里转移的问题
  • 其次就是判断有效,长度奇数,来的转移有效,而且big less 有要求
    // Problem: CF 2222 C
    // Contest: Codeforces - Spectral::Cup 2026 Round 1 (Codeforces Round 1094, Div. 1 + Div. 2)
    // URL: https://codeforces.com/contest/2222/problem/C
    // Memory Limit: 256 MB
    // Time Limit: 2000 ms
    // 
    // Powered by CP Editor (https://cpeditor.org)
    
    #include<bits/stdc++.h>
    using namespace std;
    
    
    void solve(){
        int n;
        cin>>n;
        vector<int> a(n+1);
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        vector<int> b=a;
        sort(b.begin()+1,b.begin()+n+1);
        int mid=b[(n+1)/2];
    
        vector<int> f(n+3,-1);
        f[0]=0;
        for(int i=1;i<=n;i++){
            int big=0,less=0;
            // int pre=1;
            // for(int j=1;j<=i;j++){
                // if(a[j]>mid) big++;
                // if(a[j]<mid) less++;
                // if(big==less){
                    // f[j]=max(f[j],f[pre]+1);
                    // pre=j;
                // }
            // }
    
            for(int j=i;j>=1;j--){
                if(a[j]>=mid) big++;
                if(a[j]<=mid) less++;
                int len=i-j+1;
                if(f[j-1]!=-1 && len&1 &&big>len/2 &&less>len/2){
                    f[i]=max(f[i],f[j-1]+1);
                }
            }
        }
    
        cout<<f[n]<<endl;
        return ;
    }
    
    
    int main(){
        int t;
        cin>>t;
        while(t--){
            solve();
        }
        return 0;
    }