cppAcademy
FROM LEARNING TO COMPETING

Real problems.
Your next challenge.

Explore the Group C Olympiad and Autumn Tournament archive.
Choose a topic. Find a challenge. Build your solution.

274 tasks: 213 from the Municipal, Regional, and National Olympiad rounds (2009–2026), 57 Autumn Tournament tasks (2007–2025), and 4 previously featured problems. 207 tasks have independently written English summaries, analyses, and C++17 solutions; the remaining entries link to Arena. Difficulty labels are learning estimates, not official ratings. Special formats and source clarifications are explained on affected tasks.
GROUP C2008 · HardCounting and palindrome construction

Longest Mirror Digit Sequence

Autumn Tournament (2008)

Submission name: pal.cpp

Problem summary

Select and rearrange some of the given file sizes 1,2,5,10. Concatenate their decimal representations without spaces to obtain a palindrome. Maximize the digit-string length; among maximum-length answers choose the lexicographically smallest. Print No when no nonempty palindrome can be formed.

Source clarification. The short statement does not explicitly spell out rearrangement or character-level comparison. Its samples and the official archive’s count-based reference solution establish this intended interpretation; the tokens may be reordered, and 10 contributes two decimal characters. Original test data: https://infos.infosbg.com/files/Contests/ETI/2008/C/problems/pal.zip

Input

N, followed by N values from {1,2,5,10}.

Output

The optimal palindrome as a digit string, or No.

Constraints

1<N<10000.

Worked example

Original statement sample.

Input

8
5 1 5 2 5 2 1 2

Output

1252521

Solution analysis

  1. The order may be rearranged: count the four token types. A 10 contributes a zero preceded by a one. In a palindrome, that zero must also be followed by a one. Hence using any 10 requires at least one standalone 1; with only tens no palindrome exists.
  2. Try keeping each available token count or one fewer. Removing more than one of a type cannot help repair parity: restoring a pair increases length while preserving palindrome feasibility. Also try using no tens, which is necessary when no standalone 1 is available. For each choice, the digit counts are ones+ tens, tens, twos, fives; at most one may be odd.
  3. For a feasible choice, construct its first half greedily in character order 0,1,2,5. A zero must follow a one, and the next character after a zero must be one. Before accepting a character, check that the remaining ones can separate the remaining zeros and satisfy the center boundary. Each future zero needs a preceding and following one; consecutive 101 chains share their separating ones. Other digits may have to go before such a chain when there is no spare one. The helper canFinish implements this constant-time count test.
  4. In the sample the counts are two 1s, three 2s and three 5s. Drop one 5, giving first half 125 and center 2, so the answer is 1252521. Keeping center 5 instead would produce the lexicographically larger 1255521.
  5. In canFinish, let Z be remaining half-zeros and O remaining half-ones. If the prefix ends in zero, a one is mandatory next. Otherwise Z zeros require Z ones, plus one when the previous character is not one, minus one if center1 permits the half to end in zero. If the prefix ends in one and there are other digits, a tight alternating chain needs an extra separating one when the center is 0 or 1. With no zeros left, only a zero center still requires the half to finish in one. These conditions are both necessary and constructible by placing the other digits before or after the alternating chain.

Why it works

For fixed counts, a decimal palindrome requires at most one odd digit count. If any tens are used, at least one standalone one is necessary and, together with parity, sufficient for the construction described. Retaining at most one fewer token of each type suffices because any further removed pair can be restored without breaking these conditions; the only exceptional reduction is dropping all tens when no one exists. For fixed counts, the completion test counts precisely the ones needed to surround all remaining zeros, accounting for whether the previous character already supplies a one, whether center1 allows a final zero, and whether other digits require a break in an otherwise tight alternating chain. Greedily choosing the smallest character with a feasible completion minimizes the first half, and hence the complete palindrome. Enumerating these choices and comparing length then lexicographic order yields the optimum.

Complexity: O(N) time and O(N) output storage. At most 16 parity choices plus no-ten choices are constructed, each of at most 2N digits.

Watch out. The palindrome is tested on decimal characters, not on the sequence of numeric tokens: a single 10 is not a palindrome. Treat 10 as an indivisible input token. Do not mistake this for longest palindromic subsequence in the original input order.

Commented C++17 solution

An independently written complete program with standard headers.

#include<algorithm>
#include<array>
#include<iostream>
#include<string>
using namespace std;
bool canFinish(array<int,4>remaining,char previous,char center){
    int count=remaining[0]+remaining[1]+remaining[2]+remaining[3];
    if(count==0){
        return (previous!='0'||center=='1')&&(center!='0'||previous=='1');
    }
    if(previous=='0'){
        if(remaining[1]==0){
            return false;
        }
        remaining[1]--;
        return canFinish(remaining,'1',center);
    }
    int zeros=remaining[0];
    int ones=remaining[1];
    int others=remaining[2]+remaining[3];
    if(zeros==0){
        return center!='0'||ones>0;
    }
    int needed=zeros+(previous!='1')-(center=='1');
    // Without a spare one, other digits cannot interrupt an alternating chain.
    if(previous=='1'&&others>0&&(center=='0'||center=='1')){
        needed++;
    }
    return ones>=needed;
}
int main(){
    int n;
    cin>>n;
    array<int,4>count{};
    for(int i=0;i<n;i++){
        int value;
        cin>>value;
        int index=value==1?0:value==2?1:value==5?2:3;
        count[index]++;
    }
    string best;
    for(int mask=0;mask<16;mask++){
        for(int dropTens=0;dropTens<2;dropTens++){
            auto use=count;
            bool valid=true;
            for(int i=0;i<4;i++){
                use[i]-=(mask>>i)&1;
                if(use[i]<0){
                    valid=false;
                }
            }
            if(dropTens){
                use[3]=0;
            }
            if(!valid||(use[3]>0&&use[0]==0)){
                continue;
            }
            array<int,4>digits{use[3],use[0]+use[3],use[1],use[2]};
            string symbols="0125";
            string middle;
            for(int i=0;i<4;i++){
                if(digits[i]%2==1){
                    middle+=symbols[i];
                }
            }
            if(middle.size()>1){
                continue;
            }
            array<int,4>remaining;
            for(int i=0;i<4;i++){
                remaining[i]=digits[i]/2;
            }
            string half;
            char previous='?';
            int halfLength=remaining[0]+remaining[1]+remaining[2]+remaining[3];
            for(int position=0;position<halfLength;position++){
                for(int i=0;i<4;i++){
                    char next=symbols[i];
                    if(remaining[i]==0||(next=='0'&&previous!='1')||
                        (previous=='0'&&next!='1')){
                        continue;
                    }
                    remaining[i]--;
                    if(canFinish(remaining,next,middle.empty()?'?':middle[0])){
                        half+=next;
                        previous=next;
                        break;
                    }
                    remaining[i]++;
                }
            }
            string candidate=half+middle;
            reverse(half.begin(),half.end());
            candidate+=half;
            if(candidate.size()>best.size()||(candidate.size()==best.size()&&candidate<best)){
                best=candidate;
            }
        }
    }
    cout<<(best.empty()?"No":best)<<'\n';
}

Source: Arena Olimpiici ↗