P7915 [CSP-S 2021] 回文 题解

赛时花了两个半小时在它身上。。

考场做法,自我感觉这道题推出来以后代码实现并不难

写篇题解纪念我 C S P   2021   J / S CSP\ 2021\ J/S CSP 2021 J/S 中唯二 100   p t s 100\ pts 100 pts 的题目(另一道是分糖果)。

思路

用样例 1 的第一个测试点来举例说明。

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 从左边出(它是最后一个出队的,它无论左右出队都不影响)。

这样一来,我们只要确定第一次的出队方向,其余所有的数的出队方向都可以确定。


2

出队顺序。

本人认为这是这题比较难的点。

样例中:

我们先让队列最左边的 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;
	//-//
}

AC 代码

压了一下行,加了一些注释,好看了些。

#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++~

你可能感兴趣的:(题解,算法,c++,贪心算法)