CSPS2021回文
[CSP-S 2021] 回文
题目描述
给定正整数
- 将序列
的开头元素加到 的末尾,并从 中移除。 - 将序列
的末尾元素加到 的末尾,并从 中移除。
我们的目的是让
输入格式
每个测试点包含多组测试数据。
输入的第一行,包含一个整数
第一行,包含一个正整数
第二行,包含
输出格式
对每组测试数据输出一行答案。
如果无法生成出回文数列,输出一行 ‐1
,否则输出一行一个长度为 L
或 R
构成的字符串(不含空格),其中 L
表示移除开头元素的操作 1,R
表示操作 2。
你需要输出所有方案对应的字符串中字典序最小的一个。
字典序的比较规则如下:长度均为
样例 #1
样例输入 #1
2
5
4 1 2 4 5 3 1 2 3 5
3
3 2 1 2 1 3
样例输出 #1
LRRLLRRRRL
-1
样例 #2
样例输入 #2
见附件中的 palin/palin2.in
样例输出 #2
见附件中的 palin/palin2.ans
提示
【样例解释 #1】
在第一组数据中,生成的的
另一种可能的操作方案是 LRRLLRRRRR
,但比答案方案的字典序要大。
【数据范围】
令
对所有测试点保证
测试点编号 | 特殊性质 | |||
---|---|---|---|---|
无 | ||||
无 | ||||
无 | ||||
无 | ||||
无 | ||||
有 | ||||
无 |
特殊性质:如果我们每次删除
【hack 数据提供】
@潜在了H2O下面。
解答
这道题是一个很好的思维题。我看出两个性质:
- 第一个必然是L
- 最后一个必然是L
不会了,但猜到了是栈,没想到用两个栈
看过题解后:
- 为什么要用两个栈,我们可以想到可以第一个数和数列中与之相同的数的位置作为两个断点。
借犇犇一个图
-
根据题目的意思,我们可以删数列的左边,也可以删右边。因为一开始两个断点一个是第一个放进去的,另个一个是最后一个放进去的,已经固定了。那么我们要做的就是先删里面的,再删外面的,所以对应的两个栈分别是由上到下和由下到上。
-
我们可以发现两个栈空的时候出答案
-
什么时候可以删数呢?回归题目,第一个数列可以从左往右删,因为第二个断点已经把右边锁死了,第二个数列可以从右往左删,对应的就是1的栈顶,2的栈底。我们还可以发现,假如两个相同的数分别存在于一个栈的栈顶和栈底,也是可以删的。
-
删数时如何保存答案?可以看图,然后你要发现,一个L对应一个R,R还是对应R,因为如果你在答案前半行有R的话,说明那属于4中第二种情况,对应的也是R,L就不说了(错误结论,留来警示自己的zz)
-
首先第一个数和最后一个数是不用算进去的,那么我们要清空2*(n-1),如果此时算到第i个数,那么就是2 * (n-1) - i就可以得到右端点?因为我们在最终的结果中是算上第一个数的,所以还要向右平移一位。答案为2 * (n-1) - i +1
代码
手写错误代码。
#include<bits/stdc++.h>
#define rt register int
using namespace std;
int T,n,cnt;
char s[1000005];
int a[1000005];
deque<int> q1,q2;
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d",&n);
n=n*2;
s[1]='L';
s[n]='L';
for(rt i=1;i<=n;++i)
scanf("%d",&a[i]);
int x0=a[1];
int y0;
cnt=1;
for(rt i=1;i<=n;++i)
if(a[i]==x0)y0=i;
for(rt i=2;i<y0;++i)
q1.push_back(a[i]);
for(rt i=y0+1;i<=n;++i)
q2.push_front(a[i]);
int a=q1.size(),b=q2.size();
while(!q1.empty()||!q2.empty())
{
if(q1.front()==q2.back()){
s[++cnt]='L';
q1.pop_front();
q2.pop_back();
}else if(q2.front()==q2.back())
{
s[++cnt]='R';
q2.pop_front();
q2.pop_back();
}
if(q1.size()==a&&q2.size()==b)break;
a=q1.size();b=q2.size();
cout<<a<<" "<<b<<endl;
}
if(!q1.empty()&&!q2.empty()){printf("-1\n");continue;}
else {
for(rt i=1;i<n/2;++i)
s[i+n/2]='R';
for(rt i=1;i<=n;++i)printf("%c ",s[i]);
puts("");
}
}
return 0;
}
错在两处,一个是忽略了开头是R的情况,具体可见
-
20
17 3 16 1 9 12 19 6 8 2 20 14 18 10 5 11 15 7 13 4 4 13 7 15 11 5 10 18 17 14 20 2 8 6 19 12 9 1 16 3
答案为
RRRRRRRRRRRRRRRRRRRRRRRRRRRRLRRRRRRRRRRL
其实左右的性质是对称的,这个是不用说明的,但是受到每个数的位置不是左右对称的,所以还要算以R开头的情况。
-
那个sb说L就一定对应R了,吃错药了。详见解答6
正解:来自不同犇犇
#include<bits/stdc++.h>
using namespace std;
#define rt register int
int n,T;
char res[1000005];
int a[1000005];
inline bool work(int l1,int r1,int l2,int r2) {
for(rt 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 {
--r2; --r1;
res[i]='R'; res[2*(n-1)-i+1]='L';
}
}
else {return 0;}
}
return 1;
}
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d",&n);int p1=-1,p2=-1;
for(rt i=1;i<=2*n;++i) scanf("%d",&a[i]);
for(rt i=1;i<=2*n+1;++i) res[i]=0;
for(rt i=2;i<=2*n;++i) {if(a[1]==a[i]) {p1=i; break;}}
for(rt i=1;i<2*n;++i) {if(a[2*n]==a[i]) {p2=i; break;}}
if(work(2,p1-1,p1+1,2*n)) {printf("L%sL\n",res+1);}
else if(work(1,p2-1,p2+1,2*n-1)) {printf("R%sL\n",res+1);}
else {printf("-1\n");}
}
return 0;
}
本文作者:zychh
本文链接:https://www.cnblogs.com/zychh/p/16710713.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步