1. 程式人生 > >[hihocoder1324]希爾伯特曲線

[hihocoder1324]希爾伯特曲線

這是hiho一下 第163周的題目。

題目描述

時間限制:10000ms
單點時限:1000ms
記憶體限制:256MB
描述
希爾伯特曲線是以下一系列分形曲線 Hn 的極限。我們可以把 Hn 看作一條覆蓋 2n × 2n 方格矩陣的曲線,曲線上一共有 2n × 2n 個頂點(包括左下角起點和右下角終點),恰好覆蓋每個方格一次。

hilbert-curve.png

Hn(n > 1)可以通過如下方法構造:

  1. 將 Hn-1 順時針旋轉90度放在左下角

  2. 將 Hn-1 逆時針旋轉90度放在右下角

  3. 將2個 Hn-1 分別放在左上角和右上角

  4. 用3條單位線段把4部分連線起來

對於 Hn 上每一個頂點 p ,我們定義 p 的座標是它覆蓋的小方格在矩陣中的座標,定義 p 的序號是它在曲線上從起點開始數第幾個頂點。給定 p 的座標,你能算出 p 的序號嗎?

輸入
輸入包含3個整數 n , x , y 。 n 是分形曲線的階數,(x, y)是 p 的座標。

1 ≤ n ≤ 30

1 ≤ x, y ≤ 2n

輸出
p 的序號。

樣例輸入
3 6 1
樣例輸出
60

演算法簡介

方法很簡單,就是按照題目中描述方法對曲線做切割,判斷位置是在左下角或者右上角之類的,然後根據位置切割遞迴。
容易出錯的地方在於這裡面的索引值之類的都是從1開始,所以需要n+1x,而不是nx
以及最大的結果為260,所以需要unsigned long long int。
旋轉的時候題目中所述的是n從小到大構建曲線,但遞迴是從大到小,所以旋轉方向與題目中相反。

程式碼

#include <iostream>

using namespace std;

unsigned long long int cal(unsigned long long int n,unsigned long long int x,unsigned long long int y) {
    if (n == (unsigned long long int)1) {
        if (x == 1 && y == 1)
            return 1;
        if (x == 1 && y == 2
) return 2; if (x == 2 && y == 2) return 3; if (x == 2 && y == 1) return 4; } unsigned long long int one = 1; bool bigx = (x > (one << (n-one))), bigy = (y > (one << (n-one))); unsigned long long int result; if (!bigx && !bigy) result = (one<<(2*(n-one))) - cal(n-one,(one<<(n-one))+one-y,x) + one; if (!bigx && bigy) result = (one<<(2*(n-one))) + cal(n-one,x,y-(one<<(n-one))); if (bigx && bigy) result = (one<<(2*(n-one)+one)) + cal(n-one,x-(one<<(n-one)),y-(one<<(n-one))); if (bigx && !bigy) result = (one<<(2*n)) - cal(n-one,y,((one<<(n))+one-x))+one; return result; } int main() { unsigned long long int n,x,y; cin >> n >> x >> y; cout << cal(n,x,y) << endl; return 0; }