Codeforces 1335E1 - Three Blocks Palindrome (easy version)

题面

题意

给定一个长度为 n 的数列

定义要求的回文子数列满足下图条件

其中 x 与 y 可以为 0

即这个回文子数列可以是数字完全相同的一个子数列

也可以是只包含两种数字,且其中一种平均分布在另一种数字的两侧

求出最长的回文子数列长度

解题思路

在输入时往vector里记录下每个数字出现的位置

然后开始枚举位于两侧的数字的种类 i( i = 1 ~ 26 )

首先考虑这个回文子数列只包含一种数字,刚好根据枚举

直接将答案与枚举的字符数量取大(与枚举到的vector[i].size取大)

然后如果出现的次数少于2次,说明不能放在两侧,直接跳过这次枚举

否则,再对每一侧拥有多少个数字 i 进行枚举( j = 1 ~ size/2 )

然后根据贪心可以得知,假设枚举出数字 i 在每一侧出现 j 次的话

那就可以取原数列中最左边 j 个 i 和最右边 j 个 i 作为答案的原位置,再从左数第 j 个 i 的位置 +1 开始,以右数第 j 个 i 的位置 -1 结束,在这段区间内寻找出现最多次的数字 x 作为答案的中间部分,则这种情况的答案就是 j*2+x出现的次数

优化:

(如果用的是RMQ问题的 “静态查找区间内出现最多次数字的次数” 的话可以不需要看这里的优化)

可以发现,在我们枚举 j 的时候,可行的区间永远是连续的

以【 1 1 1 2 1 2 1 1 】,枚举数字 i = 1 时为例

图中绿色区域就是每一次我们需要找出现最多次数字时的区间

由图可以得知

如果 j 从小到大枚举,则我们查找的区间会在原来的基础上缩短,即上图从上往下

如果 j 从大到小枚举,则我们查找的区间会在原来的基础上延长,即上图从下往上

所以我们可以在每次枚举 j 时,记录下查找的区间内每种数字出现的个数

在 j 改变时,把两个状态中出现改变的两端区间处理下就可以直接转移

那么这里的时间复杂度就从 O( size/2 * n ) 降为了 O(n)

详见代码

主要部分代码实现

对于枚举 j 时的转移部分

因为状态转移时左右两端都会加上或者减少一段区间

以 j 从大到小枚举为例(代码也是从大到小)

需要先处理中间部分,然后再每次增加两段子区间

所以特殊处理下中间部分

假设当前枚举的数字 i 出现的次数为 cnt

则两端每端最多有 cnt/2 个数字 i ,以 cntt=cnt/2 记录

因为vector中下标从 0 开始

所以实际范围为 0 ~ cnt-1

如果cnt是偶数,则存在两个中位数,分别是 cntt-1 和 cntt ,最开始以这两个位置开始处理即可

而如果cnt是奇数,则中位数只存在一个,因为我们要保证两端的数字个数相同,所以最中间这个数字不能取,所以最开始处理的是 cntt-1 和 cntt+1

发现实际上按照两端数字相同的性质,可以直接采用 cntt-1cnt-cntt 即可,不需要特殊判断奇偶

然后定义数组,寻找第 cntt-1 个数的位置 +1 到第 cnt-cntt 个数的位置 -1 这段范围内的各种数字出现的次数

因为两端个数为 cntt,则此时答案就是 最多次数mx+cntt*2

int num[30]={0},mx=0;
for(int j=v[i][cntt-1]+1;j<v[i][cnt-cntt];j++)
    num[ar[j]]++; //记录
for(int j=1;j<=26;j++)
    if(num[j]>mx)
        mx=num[j]; //寻找出现次数最大的
ans=max(ans,mx+cntt*2);

然后就可以通过状态转移了

j 从 cntt-1 开始向下枚举到 1

因为 j 代表的就是个数

所以每次增加的区间分别为 第 j-1 个数 到 第 j 个数第 cnt-j-1 个数 到 第 cnt-j 个数

直接加入上面的num数组即可,不需要清零

最后的答案为 mx+j*2

for(int j=cntt-1;j>0;j--)
{
    for(int k=v[i][j-1]+1;k<v[i][j];k++)
        num[ar[k]]++;
    for(int k=v[i][cnt-j-1]+1;k<v[i][cnt-j];k++)
        num[ar[k]]++;
    for(int k=1;k<=26;k++)
        if(num[k]>mx)
            mx=num[k];
    ans=max(ans,mx+j*2);
}

完整程序

感觉是纯暴力加上个优化,结果跑得飞快

范围修改下可以直接过hard

(31ms/3000ms)

#include<bits/stdc++.h>
using namespace std;
int ar[2050];
vector<int> v[30];

void solve()
{
    int n,ans=0;
    cin>>n;
    for(int i=1;i<=26;i++)
        v[i].clear(); //多组数据注意清空
    for(int i=1;i<=n;i++)
    {
        cin>>ar[i];
        v[ar[i]].push_back(i); //记录每个数字出现的位置
    }
    for(int i=1;i<=26;i++) //枚举位于两侧的数字
    {
        int cnt=v[i].size(),cntt;
        ans=max(ans,cnt); //单种数字作为答案的情况

        if(cnt<=1)
            continue; //如果只出现了一次或没出现过,直接continue即可

        cntt=cnt/2; //位于某一侧的数字的最大数量

        int num[30]={0},mx=0;

        for(int j=v[i][cntt-1]+1;j<v[i][cnt-cntt];j++) //第一次每一侧的数量均为cntt,找的是第cntt-1个到第cnt-cntt个之间的位置
            num[ar[j]]++;
        for(int j=1;j<=26;j++)
            if(num[j]>mx)
                mx=num[j]; //寻找出现次数最多的次数
        ans=max(ans,mx+cntt*2); //记录答案

        for(int j=cntt-1;j>0;j--) //然后枚举每一侧的数量
        {
            for(int k=v[i][j-1]+1;k<v[i][j];k++) //左边的区间是从第j-1个到第j个
                num[ar[k]]++;
            for(int k=v[i][cnt-j-1]+1;k<v[i][cnt-j];k++) //右边的区间是从第cnt-j-1个到第cnt-j个
                num[ar[k]]++;
            for(int k=1;k<=26;k++)
                if(num[k]>mx)
                    mx=num[k]; //寻找出现次数最多的次数
            ans=max(ans,mx+j*2); //记录答案
        }
    }
    cout<<ans<<‘\n‘;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;cin>>T;
    for(int t=1;t<=T;t++)
        solve();
    return 0;
}

原文地址:https://www.cnblogs.com/stelayuri/p/12695384.html

时间: 2024-08-30 13:00:16

Codeforces 1335E1 - Three Blocks Palindrome (easy version)的相关文章

Codeforces 1335E2 - Three Blocks Palindrome (hard version)

题面 题意/解题思路 直接延用 Easy 版本的想法即可 详解见上一篇博客Codeforces 1335E1 - Three Blocks Palindrome (easy version) 完整程序 (93ms/2000ms) #include<bits/stdc++.h> using namespace std; int ar[200050]; vector<int> v[210]; void solve() { int n,ans=0; cin>>n; for(i

Codeforces 1203F1 Complete the Projects (easy version)

cf题面 Time limit 2000 ms Memory limit 262144 kB 解题思路 看见这题觉得贪心可做,那就贪吧.(昨天真是贪心的一天,凌晨才被这两道贪心题虐,下午多校又来,感觉我现在比赛时候想贪心就是瞎猜,猜出一个结论就想办法hack,想不出hack就交上去,然后WA,然后自闭,很难摸到门道) 下面这些是我的做题和思考过程-- 显然要先把能加rating和不掉rating的做了,而且要按照要求从低到高的顺序做,因为如果当前rating能满足要求高的,那也能满足要求低的,如

Codeforces Round #540 (Div. 3) F1. Tree Cutting (Easy Version) 【DFS】

任意门:http://codeforces.com/contest/1118/problem/F1 F1. Tree Cutting (Easy Version) time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You are given an undirected tree of nn vertices. Some vert

Codeforces Round #575 (Div. 3) D1. RGB Substring (easy version)

Codeforces Round #575 (Div. 3) D1 - RGB Substring (easy version) The only difference between easy and hard versions is the size of the input. You are given a string s consisting of n characters, each character is 'R', 'G' or 'B'. You are also given a

Codeforces Global Round 7 D2. Prefix-Suffix Palindrome (Hard version) -- manacher

D2. Prefix-Suffix Palindrome (Hard version) 题目链接 manacher做法 #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; int p[N*2]; string manacher(string ss) { int len = ss.size(); string s; s.resize(len*2+2); for(int i=len;i>=0;i--) {

2016 省热身赛 Earthstone: Easy Version

Earthstone: Easy Version Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Description Earthstone is a famous online card game created by Lizard Entertainment. It is a collectible card game that revolves around turn-based mat

05-图2. Saving James Bond - Easy Version (25)

05-图2. Saving James Bond - Easy Version (25) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the world's most famous spy, was capture

05-2. Saving James Bond - Easy Version (25)

05-2. Saving James Bond - Easy Version (25) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue This time let us consider the situation in the movie "Live and Let Die" in which James Bond, the world's most famous spy, was captured

Codeforces 430A Points and Segments (easy)

题意:让你染色点,要求在给出的区间内|红色个数-蓝色个数|<=1 思路:排序后依次交替染色就能达到效果 #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> using namespace std; const int MAXN = 110; int arr[MAXN]; int n,m,x,y; int