注意,这个做法几乎没有用到任何归纳,以及任何套路的构造方法。也没有用到可能有用的置换群的构造,属于瞪眼瞪出来的。所以可扩展性极差。
一个很自然的想法是我们需要用到一些 LRDU
的“组合技”来让第 \(k\) 块更加靠近答案。
第一步观察,当我们的块是最中间的时候,只需要一步 DL
即可。但似乎没什么用。
仔细思考一下,我们想把一个块不断地往前挪,等价于把它前面的一个数挪到后面去。那么很自然的想法是把数卡到第一个黑格子上面,然后不让它往下走,再把它放到右边去,也就是 LDRU
。
你惊喜的试了几次,发现好像不断执行 LDRU
就好了,可是当你试到后面时候发现不太行,原因是卡到了中间黑方格或后面下不来了。
现在我们还是需要往前挪,在通过一些手玩之后,还有一步比较有用的组合:LDLU
。这可以把一些数卡到左边下面,这样再执行一遍 DRUR
就可以把原先的数放到等价于中间位置的那个地方了。
你又试了几次,发现太靠后的地方还是不太行,原因是会在 U
的那一步被卡住。换一种思路,把需要搞得数放到最右边,其他数卡到下边。
执行若干次 LDLU
直到它到了中间,然受执行 LDRD
,把所求卡到右边下面,然后不断执行 ULDL
,这样把左边上面也卡满。发现这样最后一个一定是所求的放个,最后再执行一遍 RDL
。
分析一下,一开始会做 \(\frac{n}{2}\) 次 LDLU
,然后会做 \(\frac{n}{2}\) 次 ULDL
,于是总次数是 \(4n+O(1)\) 的。
我以为我的做法很菜,但是标算好像也是 \(4n+O(1)\) 步,那没事了。
#include <bits/stdc++.h>
using namespace std;
int n, k;
int main()
{
cin >> n >> k;
if (k <= n / 2)
{
for (int i = 1; i < k; i++) printf("LDRU");
printf("L");
}
else if (k <= n / 2 + (n / 2) / 2)
{
for (int i = 1; i < k - n / 2; i++) printf("LDLU");
printf("DRUR");
for (int i = 1; i < n / 2; i++) printf("LDRU");
printf("L");
}
else
{
for (int i = 1; i < k - n / 2; i++) printf("LDLU");
printf("LDRD");
for (int i = 1; i < n / 2; i++) printf("ULDL");
puts("RDL");
}
return 0;
}