老规矩,献上题目:
题目描述
给定正整数 n 和整数序列 a1,a2,…,a2n,在这 2 n2n 个数中, n1,2,…,n 分别各出现恰好 2 次。现在进行 2n 次操作,目标是创建一个长度同样为 2n 的序列 b1,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。
你需要输出所有方案对应的字符串中字典序最小的一个。
字典序的比较规则如下:长度均为 2 n 的字符串 s 1∼2n 比t 1∼2n 字典序小,当且仅当存在下标1≤k≤2n 使得对于每个 1≤i<k 有si=ti 且 sk<tk。
输入输出样例
输入 #1
2 5 4 1 2 4 5 3 1 2 3 5 3 3 2 1 2 1 3
输出 #1
LRRLLRRRRL -1
题解:
根据题意:
我们首先要明确一点:能选左边就尽量不要选右边,因为题目要求输出字典序最小的一组答案,L肯定优先于R;
其次,如果能构成b数组的话,那么输出的答案的最后一个字符一定是'L',这个不用我多说,因为选到最后只有一个字符,我们既可以把它当作左边,也可以把它当作右边。那么我们肯定要选左边啊,这样尽可能满足字典序小。
对待这道题,我们依然有2种方法:
骨灰级做法:枚举出所有可能的情况(O(2^N)),不用我说你也知道肯定要TEL掉的,我们对这种无脑方法一概而过
妙哉:我们可以内外同时开工:
最先开始,我们有两种情况——选最左边或者是最右边,我们优先选择最左边,如果左边不行再考虑右边。
我们以左边为例展开讨论(右边和左边的思路一模一样,不再累述)>>>>
给定一个长度为2n的序列N,建立数组S表示我们的答案,由’‘L’R‘组成;序列最左边记waileft,数组最右边记为wairight
初始状态下,外层waileft下标为1,wairight下标为2n
我们设最左边的数为X,取出X;然后在剩下的序列中找出与X相等的值,记作它们的下标分别为 i1,i2,记i2左边的数为neileft,i2右边的数记为neiright
取出X后,外层left下标变为i1+1,右边right不变还是2n
接下来我们就要两边同时开工了,怎么开工呢?
上述操作后,我们已经找出与X相等值的下标。由于我们最终得到的是回文序列,那么这个回文序列无论是从左到右读还是从右到左读,顺序都是一样的,容易知道,最终回文序列的两端都是X,那么回文序列临近X的一组相等的值肯定在原序列中一个在外层的最左边waileft或最右边wairight,一个在下标为i2的左边或右边(neileft or neiright)。
那好了,有四种情况:
1. N[waileft]=N[neileft]
2. N[waileft]=N[neiright]
3. N[wairight]=N[neileft]
4 N[wairight]=N[neiright]
注意:这四种情况的顺序我可不是随意写的,它必须是严谨的。为什么这么说呢?
因为上述的四种情况就是4种操作:
满足对应的情况,我们就进行相应的操作
1. L L
2. L R
3. R L
4. R R
我们还是要遵循字典序最小的原则嘛
那要是四种情况都不满足呢?那就说明这种情况不成立。我们要重新选择最右边再来,如果也不成立,那就说明这个序列不能形成回文,果断输出-1,结束程序。这是判断误解,减少冗余运算的关键一点!
这样我们判断一次就可以同时填补S数组的两端的一个值,不断向内填补空白。
如何填补?
最先开始选择左边那么S[1]=L ,S[2n]=L
如果选择右边 S[1]=R ,S[2n]=L
不管是外层还是内层,只要选择左边(waileft or neileft),我们都记作L;
只要选择右边(wairight or neiright),我们都记作R
分别由两边向内填补,这样如果能够填满,它一定是正确的答案。
完整的代码如下
Code(100 ptx)
#include<cstdio>
int num[1000005];
char s[1000005];
int tot,t;
int main(){
scanf("%d",&tot);
while(tot--){
scanf("%d",&t);
int sum=2*t,nei;
for(int i=1;i<=sum;i++)
scanf("%d",&num[i]);
for(int i=2;i<=sum;i++){
if(num[1]==num[i]){
nei=i;
break;
}
}
int wai_left=2,wai_right=sum,nei_left=nei-1,nei_right=nei+1;
int start=1,end=sum;
s[start]='L';s[end]='L';
while(1){
if(start==end-1){
s[sum+1]='\0';
printf("%s\n",s+1);
break;
}
if(num[wai_left]==num[nei_left] && wai_left<nei_left){
s[++start]='L';s[--end]='L';
wai_left++;nei_left--;
}
else if(num[wai_left]==num[nei_right] ){
s[++start]='L';s[--end]='R';
wai_left++;nei_right++;
}
else if(num[wai_right]==num[nei_left]){
s[++start]='R';s[--end]='L';
wai_right--;nei_left--;
}
else if(num[wai_right]==num[nei_right] && wai_right>nei_right){
s[++start]='R';s[--end]='R';
wai_right--;nei_right++;
}
else break;
}
if(start==end-1)continue;
for(int i=1;i<sum;i++){
if(num[sum]==num[i]){
nei=i;
break;
}
}
wai_left=1;wai_right=sum-1;nei_left=nei-1;nei_right=nei+1;
start=1,end=sum;
s[start]='R';s[end]='L';
while(1){
if(start==end-1){
s[sum+1]='\0';
printf("%s\n",s+1);
break;
}
if(num[wai_left]==num[nei_left] && wai_left<nei_left){
s[++start]='L';s[--end]='L';
wai_left++;nei_left--;
}
else if(num[wai_left]==num[nei_right]){
s[++start]='L';s[--end]='R';
wai_left++;nei_right++;
}
else if(num[wai_right]==num[nei_left]){
s[++start]='R';s[--end]='L';
wai_right--;nei_left--;
}
else if(num[wai_right]==num[nei_right] && wai_right>nei_right){
s[++start]='R';s[--end]='R';
wai_right--;nei_right++;
}
else{
putchar('-');putchar('1');putchar('\n');
break;
}
}
}
return 0;
}
程序测评结果如下:
但是我第一次提交时的代码如下:
Code(first)
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=500005;
int n;
int a[maxn*2];
char res[maxn*2];
bool work(int l1,int r1,int l2,int r2)
{
for(int i=1;i<n;i++)
{
if((l1<=r1)&&(((l2<=r2)&&(a[l1]==a[l2]))||((l1<r1)&&(a[l1]==a[r1]))))
{
if((l1<r1)&&(a[l1]==a[r1]))
{
l1++;
r1--;
res[i]='L';
res[2*(n-1)-i+1]='L';
}
else
{
l1++;
l2++;
res[i]='L';
res[2*(n-1)-i+1]='R';
}
}
else if((l2<=r2)&&(((l1<=r1)&&(a[r2]==a[r1]))||((l2<r2)&&(a[l2]==a[r2]))))
{
if((l2<r2)&&(a[l2]==a[r2]))
{
l2++;
r2--;
res[i]='R';
res[2*(n-1)-i+1]='R';
}
else
{
r1--;
r2--;
res[i]='R';
res[2*(n-1)-i+1]='L';
}
}
else
{
return false;
}
}
return true;
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=2*n;i++)
{
scanf("%d",&a[i]);
}
int l1=0;
int l2=0;
for(int i=2;i<=2*n;i++)
{
if(a[i]==a[1])
{
l1=i;
break;
}
}
for(int i=1;i<2*n;i++)
{
if(a[i]==a[2*n])
{
l2=i;
break;
}
}
if(work(2,l1-1,l1+1,2*n))
{
printf("L%sL",res+1);
cout<<endl;
}
else if(work(1,l2-1,l2+1,2*n-1))
{
printf("R%sL",res+1);
cout<<endl;
}
else
{
cout<<"-1"<<endl;
}
}
return 0;
}
评测结果如下:
坑了我半个多小时。读者可以比较两个代码的区别。只是多加了两行
s[sum+1]='\0';
否则有可能会把上一个结果的残余项输出出来,必须要一个结束符号!为你们敲响警钟了.....
End~~~~
若有不懂的地方,欢迎评论下方咨询。
写作不易,谢谢大家支持!