UOJ 152 漢諾塔 分治

2022-05-22 08:48:13 字數 1487 閱讀 3360

題目鏈結

題意:有三根編號為\((1, \, 2, \, 3)\)的柱子,然後第一根柱子上有編號為\(1 \sim n(n \leq 10000)\)的盤子,從上到下第\(i\)個盤子的編號是\(a_i\),其他兩根柱子是空的。

你可以進行一種操作x y,表示將第\(x\)根柱子最上面的盤子放到第\(y\)根柱子的最上面去。

輸出不超過\(10^6\)次操作,使得最終所有的盤子都在同一根柱子(柱子的編號不限)上而且從上到下編號是遞增的。

分析:首先很容易想到乙個\(o(n^2)\)的做法:

就是每次遇到不是\(n\)的盤子的時候就扔到柱子\(3\)上去,否則就扔到柱子\(2\)上去。

然後重複這一過程,在\(n-1\)號盤子所在的柱子上,遇到不是\(n-1\)的就扔到其他柱子上,遇到\(n-1\)就扔到柱子\(2\),也就是\(n\)號盤子的上面。

然後有乙個\(o(n^})\)的做法。是基於平方分割的思想,每次取出\(\sqrt\)個編號最大的盤子,然後暴力用\(o((\sqrt)^2)=o(n)\)次操作將其排序。

最後是\(o(nlogn)\)的做法:

這種做法是基於分治的思想,和快排的思路一模一樣:先分解問題然後再合併問題。

我們規定從上到下遞增的是順序,從上到下遞減的是逆序

分:

對於某個柱子上亂序的區間\([l, \, r]\),如果我們要將它順序排序,我們可以將\([l, \, mid]\)中的盤子和\([mid+1, \, r]\)中的盤子分別扔到其他兩根柱子上。

然後對這兩個根柱子上的盤子逆序排序。

合:

左右區間逆序排好序後然後按照從大到小的順序再放回原來的柱子上,使得整個\([l, \, r]\)是順序的。

對\([l, \, mid]\)和\([mid+1, \, r]\)這兩個區間逆序排序,這樣問題的規模就減小了一半。

相反地,如果要對乙個區間逆序排序,就要先對它的左右區間順序排序,然後合併。

#include #include #include using namespace std;

const int maxn = 10000 + 10;

int n;

int cnt[3], t[3][maxn];

int lft = ;

int rgh = ;

void op(int i, int j)

void solve(int cur, int l, int r, bool inv)

solve(lft[cur], l, mid, !inv);

solve(rgh[cur], mid+1, r, !inv);

if(!inv) else

}int ans;

void dfs(int t)

int main()

UOJ 152 漢諾塔 題解

三根柱子,n nn 個盤子,編號 1 11 n nn,開始時盤子亂序套在一根柱子上。構造一種方案,用 106 10 6 106 以內步數使所有盤子以遞增序套在一根柱子上。n 1 04 n leq 10 4 n 10 4這個題的目的是要把盤子排序。1 04 10 4 104 和 106 10 6 10...

UOJ 152 盤子序列

題目描述 有n 個盤子。盤子被生產出來後,被按照某種順序摞在一起。初始盤堆中如果乙個盤子比所有它上面的盤子都大,那麼它是安全的,否則它是危險的。稱初始盤堆為a,另外有乙個開始為空的盤堆 b。為了掩蓋失誤,生產商會對盤子序列做一些 處理 每次進行以下操作中的乙個 1 將a 最上面的盤子放到 b 最上面...

分治演算法 漢諾塔問題

package edu.zcc.divideandconquer 漢諾塔問題是乙個簡單的分治法問題,2個以上高度的漢諾塔都可以看作只有兩個盤,最下面乙個盤p和最後乙個盤上面的所有盤q,第一步 將q從第乙個柱子移動到第二個柱子上 第二步 將p從第乙個柱子移動到第三個柱子上 第三步 將q從第二個柱子移動...