[CSP-S 2021] 回文


題目描述:

給定正整數 n 和整數序列 a1, a2,…,a2n,在這 2n 個數中,1, 2,…,n 分別各出現恰好 2 次。現在進行 2n 次操作,目標是創建一個長度同樣為 2n 的序列 b 1,b2,…,b2n,初始時 b 為空序列,每次可以進行以下兩種操作之一:

將序列 a 的開頭元素加到 b 的末尾,並從 a 中移除。
將序列 a 的末尾元素加到 b 的末尾,並從 a 中移除。

我們的目的是讓 b 成為一個回文數列,即令其滿足對所有 1≤i≤n,有 bi=b2n+1−i。
請你判斷該目的是否能達成,如果可以,請輸出字典序最小的操作方案,具體在【輸出格式】中說明。

輸入格式:

每個測試點包含多組測試數據。

輸入的第一行,包含一個整數 T,表示測試數據的組數。對於每組測試數據:

第一行,包含一個正整數 n。
第二行,包含 2n 個用空格隔開的整數 a1,a2,…,a2n 。

輸出格式:

對每組測試數據輸出一行答案。

如果無法生成出回文數列,輸出一行 ‐1,否則輸出一行一個長度為 2n 的、由字符 L 或 R 構成的字符串(不含空格),其中 L 表示移除開頭元素的操作 1,R 表示操作 2。

你需要輸出所有方案對應的字符串中字典序最小的一個。

字典序的比較規則如下:長度均為 2n 的字符串 s 1∼2n 比 t 1∼2n字典序小,當且僅當存在下標 1≤k≤2n 使得對於每個 1≤i<k 有 s i=t i 且 s k < t k。

數據范圍:

 

   一道有趣的構造題。

   以左端點為例分析一下:

 

     假設紅圈出數值相同,那么顯然對於 左邊一半 會 從左往右 取,右邊一半 會 從右往左 取(如綠色箭頭)。這樣出來的答案是正着的。

 

 

      如果我們從中間開始往兩側去(如黃色箭頭),這樣出來的答案正好是 倒的。

    

 

      現在我們可以取的是左右端點(即綠色點),那么,如果有解,其對應點一定位於中間點的相鄰兩側(即藍色點)。

 

  想到這,就很簡單了。

  由於要字典序最小,所以優先考慮取左端點,若無解再考慮右端點,若還無解則改題無解。

  (詳見代碼)

 

 

Code:

回文

 

UpData(2021.11.1):

  以上做法(包括Code)均可通過民間數據,但官方數據對此做法進行了一定的hack:構造一些 “看上去” 可行的無解情況,讓搜索樹跑滿。

  這個時候我們可優化一下,比如剪枝,當然計時也是可以的(我是計的時)。

 

New Code:

#include<bits/stdc++.h>
using namespace std;
int Step,_,n,A[1000005],Nxt[1000005],Lst[1000005],Ans[2][500005];
#define gc (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1,*p2;
inline int read()
{
    char ch;int x(0);while((ch=gc)<48);
    do x=x*10+ch-48;while((ch=gc)>=48);
    return x;
}
inline bool DFS(int a,int b,int c,int d)
{
    if(++Step>500000) return 0;
    if(a>b&&c>d) return 1;
    if(a<=b)
    {
        if(a!=b&&A[a]==A[b]&&DFS(a+1,b-1,c,d)) {Ans[0][++Ans[0][0]]=1,Ans[1][++Ans[1][0]]=1;return 1;}
        if(c<=d&&A[a]==A[c]&&DFS(a+1,b,c+1,d)) {Ans[0][++Ans[0][0]]=1,Ans[1][++Ans[1][0]]=2;return 1;}
    }
    if(c<=d)
    {
        if(a<=b&&A[d]==A[b]&&DFS(a,b-1,c,d-1)) {Ans[0][++Ans[0][0]]=2,Ans[1][++Ans[1][0]]=1;return 1;}
        if(c!=d&&A[d]==A[c]&&DFS(a,b,c+1,d-1)) {Ans[0][++Ans[0][0]]=2,Ans[1][++Ans[1][0]]=2;return 1;}
    }
    return 0;
}
inline void Print(int x)
{
    if(x==1) printf("L");
    else printf("R");
    for(register int i=Ans[0][0];i;--i)
        if(Ans[0][i]==1) printf("L") ;
        else printf("R");
    for(register int i=1;i<=Ans[1][0];++i)
        if(Ans[1][i]==1) printf("L");
        else printf("R");
    printf("L\n");
}
int main()
{
    _=read();
    for(register int __=1;__<=_;++__)
    {
        n=read(),Step=0;for(register int i=1;i<=n;++i) Nxt[i]=Lst[i]=0;Ans[0][0]=Ans[1][0]=0,n*=2;
        for(register int i=1;i<=n;++i)
        {
            A[i]=read();
            if(Nxt[A[i]]) Lst[A[i]]=i;
            else Nxt[A[i]]=i;
        }
        if(DFS(2,Lst[A[1]]-1,Lst[A[1]]+1,n)) Print(1);
        else if(DFS(1,Nxt[A[n]]-1,Nxt[A[n]]+1,n-1)) Print(2);
        else printf("-1\n");
    }
    return 0;
}
New

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM