赛时花了两个半小时在它身上。。
考场做法,自我感觉这道题推出来以后代码实现并不难。
写篇题解纪念我 C S P 2021 J / S CSP\ 2021\ J/S CSP 2021 J/S 中唯二 100 p t s 100\ pts 100 pts 的题目(另一道是分糖果)。
用样例 1 的第一个测试点来举例说明。
出队方向。
容易发现,当我们决定了第一个数字是从左右哪边出来的,每一个数字的出队方式就已经确定好了。
样例中:
假如第一次从左边出,那么每一个数字的出队情况如下所示:
4 1 2 4 5 3 1 2 3 5
L L L L R R R R R R
原因:
既然我们先让最左边的 4 出队了,那么排在第 4 位(从左往右)的第二个 4 就必须是最后一个出去(因为要形成回文)。此时,这个 4 右边的 5 如果仍然是从左边出去,证明 5 右边的 4 已经从左边出去了,与前面“第二个 4 必须是最后一个出去的”相矛盾,说明这个 5 必须得从右边出去。
其他的数同理,可确定下来出队方向。
又为了保证字典序最小,所以我们要让第二个 4 从左边出(它是最后一个出队的,它无论左右出队都不影响)。
这样一来,我们只要确定第一次的出队方向,其余所有的数的出队方向都可以确定。
出队顺序。
本人认为这是这题比较难的点。
样例中:
我们先让队列最左边的 4 出队,那么就有两个数的出队顺序确定了。
4 1 2 4 5 3 1 2 3 5
1 \ \ 10 \ \ \ \ \ \
(按出队顺序先后从 1 1 1 到 n × 2 n \times 2 n×2 编号。)
如何确定下一个出队的数?
按字典序最小的原则,我们让 1 先出队。此时:
4 1 2 4 5 3 1 2 3 5
1 2 \ 10 \ \ 9 \ \ \
这样,按照我们 1 中已推出的结论,就会发现被 4 和 1 夹在中间的 5 和 3 没法出队了。
那换个方向,让 5 出队:
4 1 2 4 5 3 1 2 3 5
1 \ \ 10 9 \ \ \ \ 2
此时最后两个出队的 4 和 5 是相邻的,不会造成矛盾或无法出队的情况。
综上,在求出队顺序时,我们把最后出队的、相邻的、在队列中间的几个数看成一个“块”。
每次只有队头(或队尾)即将要出队的数的“另一个自己”紧贴着这个块(即在这个块的左边一位或者右边一位),这个数才可以出队。
不然会造成中间夹着数无法出队的情况。
这样以来,我们可以确定好每个数的出队方向和出队顺序,这道题的思路就到此结束了。
还不理解的可以拿样例手推一下,很容易可以想通。
这道题真的挺考验选手的代码实现能力的。
我们在输入的时候记录下每一个数字出现的两个位置。按下标大小记录好,后面用的时候很容易搞混。
按照字典序尽量小的原理,我们优先让最左边的数出队。
维护两对指针,一对记录输入队列的头尾,一对记录“块”的左右下标。
每一次看队头或队尾的“第二位置”是否在块的相邻地方,是就出队。
如果队头队尾都无法出队,就失败了,如果最开始是从左边出的,就试试从右边出;如果最开始已经是从右边出去的了,就只能输出 -1 了。
输出结果的话,按照出队顺序排个序,再把出队方向输出即可。
这样一来,这道题就做完了 (切掉了)~
真的臭,勉强能看吧。
#include
using namespace std;
#define rint register int
const int maxn = 1e6 + 5;
int T, n;
int a[maxn];
bool c[maxn >> 1];
struct node{
int fx, nm;
}e[maxn];
struct node2{
int fr, sc;
}b[maxn >> 1];
int l, r;
int h, t, flg;
inline void init ()
{
memset (e, 0, sizeof e);
memset (b, 0, sizeof b);
memset (c, 0, sizeof c);
l = r = h = t = flg = 0;
}
inline int read ()
{
int s = 0, x = 1;
char ch = getchar ();
while (ch < '0' or ch > '9')
{
if (ch == '-')
x = -1;
ch = getchar ();
}
while (ch >= '0' and ch <= '9')
s = s * 10 + ch - '0', ch = getchar ();
return s * x;
}
inline void dl ()
{
int cnt = 1;
while (cnt < n and h <= t)
{
if (b[a[h]].sc == l - 1 and !e[b[a[h]].sc].nm)
{
e[h].fx = 1, e[h].nm = ++cnt;
e[b[a[h]].sc].fx = 1, e[b[a[h]].sc].nm = 2 * n - cnt + 1;
l -= 1, h += 1;
}
else if (b[a[h]].sc == r + 1 and !e[b[a[h]].sc].nm)
{
e[h].fx = 1, e[h].nm = ++cnt;
e[b[a[h]].sc].fx = 2, e[b[a[h]].sc].nm = 2 * n - cnt + 1;
r += 1, h += 1;
}
else if (b[a[t]].fr == l - 1 and !e[b[a[t]].fr].nm)
{
e[t].fx = 2, e[t].nm = ++cnt;
e[b[a[t]].fr].fx = 1, e[b[a[t]].fr].nm = 2 * n - cnt + 1;
l -= 1, t -= 1;
}
else if (b[a[t]].fr == r + 1 and !e[b[a[t]].fr].nm)
{
e[t].fx = 2, e[t].nm = ++cnt;
e[b[a[t]].fr].fx = 2, e[b[a[t]].fr].nm = 2 * n - cnt + 1;
r += 1, t -= 1;
}
else
{
flg = 1;
break;
}
}
}
inline bool cmp (node x, node y)
{
return x.nm < y.nm;
}
inline void otpt ()
{
sort (e + 1, e + (n << 1) + 1, cmp);
for (rint i (1); i <= (n << 1); ++i)
{
if (e[i].fx == 1)
printf ("L");
else if (e[i].fx == 2)
printf ("R");
}
printf ("\n");
}
int main ()
{
// freopen ("palin.in", "r", stdin); freopen ("palin.out", "w", stdout);
T = read ();
while (T--)
{
init ();
n = read ();
for (rint i (1); i <= (n << 1); ++i)
{
a[i] = read ();
if (!c[a[i]])
b[a[i]].fr = i, c[a[i]] = 1;
else b[a[i]].sc = i;
}
//left begin
e[1].fx = 1, e[1].nm = 1;
e[b[a[1]].sc].fx = 1, e[b[a[1]].sc].nm = 2 * n;
l = r = b[a[1]].sc;
h = 2, t = 2 * n;
flg = 0;
dl ();
if (!flg)
{
otpt ();
continue;
}
//right begin
memset (e, 0, sizeof e);
e[n << 1].fx = 2, e[n << 1].nm = 1;
e[b[a[n << 1]].fr].fx = 1, e[b[a[n << 1]].fr].nm = 2 * n;
l = r = b[a[n << 1]].fr;
h = 1, t = 2 * n - 1;
flg = 0;
dl ();
if (!flg)
{
otpt ();
continue;
}
else printf ("-1\n");
}
return 0;
//-//
}
压了一下行,加了一些注释,好看了些。
#include
using namespace std;
#define rint register int
const int maxn = 1e6 + 5;
int T, n, a[maxn];
bool c[maxn >> 1];
struct node{
int fx, nm;//出队方向、出队顺序的编号
}e[maxn];
struct node2{
int fr, sc;//第一次出现的下标,第二次出现的下标
}b[maxn >> 1];
int l, r, h, t, flg;
inline void init ()
{
memset (e, 0, sizeof e);
memset (b, 0, sizeof b);
memset (c, 0, sizeof c);
l = r = h = t = flg = 0;
}
inline int read ()
{
int s = 0, x = 1;
char ch = getchar ();
while (ch < '0' or ch > '9'){if (ch == '-') x = -1; ch = getchar (); }
while (ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar ();
return s * x;
}
inline void dl ()
{
int cnt = 1;//记录已经处理了的数的数量
while (cnt < n and h <= t)
{
if (b[a[h]].sc == l - 1 and !e[b[a[h]].sc].nm)//注意 if 条件的先后顺序,要保证字典序最小
{
e[h].fx = 1, e[h].nm = ++cnt;
e[b[a[h]].sc].fx = 1, e[b[a[h]].sc].nm = 2 * n - cnt + 1;
l -= 1, h += 1;
}
else if (b[a[h]].sc == r + 1 and !e[b[a[h]].sc].nm)
{
e[h].fx = 1, e[h].nm = ++cnt;
e[b[a[h]].sc].fx = 2, e[b[a[h]].sc].nm = 2 * n - cnt + 1;
r += 1, h += 1;
}
else if (b[a[t]].fr == l - 1 and !e[b[a[t]].fr].nm)
{
e[t].fx = 2, e[t].nm = ++cnt;
e[b[a[t]].fr].fx = 1, e[b[a[t]].fr].nm = 2 * n - cnt + 1;
l -= 1, t -= 1;
}
else if (b[a[t]].fr == r + 1 and !e[b[a[t]].fr].nm)
{
e[t].fx = 2, e[t].nm = ++cnt;
e[b[a[t]].fr].fx = 2, e[b[a[t]].fr].nm = 2 * n - cnt + 1;
r += 1, t -= 1;
}
else
{
flg = 1;
break;
}
}
}
inline bool cmp (node x, node y)
{
return x.nm < y.nm;
}
inline void otpt ()//输出
{
sort (e + 1, e + (n << 1) + 1, cmp);
for (rint i (1); i <= (n << 1); ++i)
if (e[i].fx == 1) printf ("L");
else if (e[i].fx == 2) printf ("R");
printf ("\n");
}
int main ()
{
T = read ();
while (T--)
{
init ();
n = read ();
for (rint i (1); i <= (n << 1); ++i)
{
a[i] = read ();
if (!c[a[i]])
b[a[i]].fr = i, c[a[i]] = 1;
else b[a[i]].sc = i;//保证 fr 小于 sc
}
//left begin
e[1].fx = 1, e[1].nm = 1;
e[b[a[1]].sc].fx = 1, e[b[a[1]].sc].nm = 2 * n;
l = r = b[a[1]].sc;
h = 2, t = 2 * n;
flg = 0, dl ();//这里的 flag 也要清空,考场这个看了十几分钟
if (!flg)
{
otpt ();
continue;
}
//right begin
memset (e, 0, sizeof e);//一定要记得初始化
e[n << 1].fx = 2, e[n << 1].nm = 1;
e[b[a[n << 1]].fr].fx = 1, e[b[a[n << 1]].fr].nm = 2 * n;//注意 fr 和 sc
l = r = b[a[n << 1]].fr;
h = 1, t = 2 * n - 1;
flg = 0, dl ();
if (!flg)
{
otpt ();
continue;
}
else printf ("-1\n");
}
return 0;
}
期中考 RP++~