注意,這個做法幾乎沒有用到任何歸納,以及任何套路的構造方法。也沒有用到可能有用的置換羣的構造,屬於瞪眼瞪出來的。所以可擴展性極差。
一個很自然的想法是我們需要用到一些 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;
}