簡體   English   中英

解決黑客地球問題的算法

[英]Algorithm to solve a Hacker earth problem

我一直在研究 Hackerearth 問題。 這是問題陳述:
我們有三個變量 a、b 和 c。 我們需要將 a 轉換為 b 並且允許以下操作:
1.可以減1。
2.可以減2。
3. 可以乘以c。
將 a 轉換為 b 所需的最少步驟。
這是我想出的算法:
將計數增加到 0。
循環直到 a === b:
1. 執行 (x = a * c)、(y = a - 1) 和 (z = a - 2)。
2、在x、y、z中,選擇與b絕對差最小的那個。
3. 將 a 的值更新為在 x、y 和 z 中選擇的值。
4. 將計數加 1。
我可以通過基本測試用例,但我所有的高級用例都失敗了。 我想我的邏輯是正確的,但由於復雜性,它似乎失敗了。
有人可以建議更優化的解決方案。
編輯 1
示例代碼

function findMinStep(arr) {
    let a = parseInt(arr[0]);
    let b = parseInt(arr[1]);
    let c = parseInt(arr[2]);
    let numOfSteps = 0;
    while(a !== b) {
        let multiply = Math.abs(b - (a * c));
        let decrement = Math.abs(b - (a - 1));
        let doubleDecrement = Math.abs(b - (a - 2));
        let abs = Math.min(multiply, decrement, doubleDecrement);
        if(abs === multiply) a = a * c;
        else if(abs === decrement) a -= 1; 
        else a -= 2;
        numOfSteps += 1;
    }
    return numOfSteps.toString()
}

樣本輸入: a = 3,b = 10,c = 2
解釋: 3 乘以 2 得到 6,6 減 1 得到 5,5 乘以 2 得到 10。
對 Python 和 JS 都打上標簽的原因:兩者都適合,但我不是在尋找代碼,只是在尋找優化的算法和分析思維。

編輯2:

function findMinStep(arr) {
    let a = parseInt(arr[0]);
    let b = parseInt(arr[1]);
    let c = parseInt(arr[2]);
    let depth = 0;
    let queue = [a, 'flag'];
    if(a === b ) return 0
    if(a > b) {
        let output = Math.floor((a - b) / 2);
        if((a - b) % 2) return output + 1;
        return output

    }
    while(true) {
        let current = queue.shift();
        if(current === 'flag') {
            depth += 1;
            queue.push('flag');
            continue;
        }
        let multiple = current * c;
        let decrement = current - 1;
        let doubleDecrement = current -2;
        if (multiple !== b) queue.push(multiple);
        else return depth + 1
        if (decrement !== b) queue.push(decrement);
        else return depth + 1
        if (doubleDecrement !== b) queue.push(doubleDecrement);
        else return depth + 1
    }
}

仍然超時。 還有什么建議嗎?
問題鏈接供您參考。

BFS

貪婪的方法在這里不起作用。

然而,它已經在正確的軌道上。 考慮圖G ,其中每個節點代表一個值,每條邊代表一個操作,並連接與該操作相關的兩個值(例如:4 和 3 通過“減 1”連接)。 使用此圖,我們可以輕松執行BFS 搜索以找到最短路徑:

def a_to_b(a, b, c):
    visited = set()
    state = {a}
    depth = 0

    while b not in state:
        visited |= state
        state = {v - 1 for v in state if v - 1 not in visited} | \
                {v - 2 for v in state if v - 2 not in visited} | \
                {v * c for v in state if v * c not in visited}

        depth += 1

    return 1

這個查詢系統地測試所有可能的操作組合,直到它通過逐步測試到達b 即從a生成可以通過單個操作達到的所有值,然后測試通過兩次操作可以達到的所有值,依此類推,直到b在生成的值中。

深入分析

(假設c >= 0 ,但可以推廣)

到目前為止,對於幾乎沒有分析的標准方法。 這種方法的優點是它適用於任何此類問題並且易於實施。 但是,它的效率不是很高,並且一旦數字增長,就會相當快地達到極限。 因此,我將展示一種深入分析問題並獲得(遠遠)性能更高的解決方案的方法:

在第一步中,此答案將分析問題:

我們需要操作-->op使得a -->op b-->op是一個序列

  • 減1
  • 減2
  • 乘以c

首先,如果我們先減然后乘會發生什么?

(a - x) * c = a * c - x * c

如果我們先乘后減,接下來會發生什么?

a * c - x'

定位系統

好吧,這沒有簡化的轉換。 但是我們已經有了分析更復雜的操作鏈的基本部分。 讓我們看看當我們交替地鏈式減法和乘法時會發生什么:

(((a - x) * c - x') * c - x'') * c - x'''=
((a * c - x * c - x') * c - x'') * c - x''' =
(a * c^2 - x * c^2 - x' * c - x'') * c - x''' =
a * c^3 - x * c^3 - x' * c^2 - x'' * c - x'''

看起來很熟悉? 我們距離在基於c位置系統中定義ab之間的差異僅一步之遙:

a * c^3 - x * c^3 - x' * c^2 - x'' * c - x''' = b
x * c^3 + x' * c^2 + x'' * c + x''' = a * c^3 - b

不幸的是,以上仍然不是我們所需要的。 我們只能說,方程的 LHS 將始終>=0 一般來說,我們首先需要推導出適當的指數n (在上面的例子中為 3), st 它是最小的、非負的並且a * c^n - b >= 0 為單個系數 ( x , x' , ...) 解決這個問題,其中所有系數都是非負的,這是一項相當簡單的任務。

我們可以從上面展示兩件事:

  • 如果a < ba < 0 ,則無解
  • 如上所述求解並將所有系數轉換為適當的操作導致最優解

最優性證明

上面的第二個陳述可以通過對n的歸納來證明。

n = 0 : 在這種情況下a - b < c ,所以只有一個-->op

n + 1 :讓d = a * c^(n + 1) - b d' = d - m * c^(n + 1) ,其中選擇m ,使得d'最小且非負。 每個歸納假設d'可以通過位置系統最佳地生成。 留下正好m * c^n的差異。 通過低階項不能比通過m / 2減法更有效地覆蓋這種差異。

算法(TLDR 部分)

考慮a * c^n - b作為數基c並嘗試找到它的數字。 最后的數字應該有n + 1位數字,其中每個數字代表一定數量的減法。 多個減法通過將減法值相加由一位數表示。 例如 5 表示-2 -2 -1 從最重要到最不重要的數字,該算法的操作如下:

  1. 執行由數字指定的減法
  2. 如果當前數字是最后一位,則終止
  3. 乘以c並從 1. 與下一個數字重復

例如:

a = 3, b = 10, c = 2
選擇 n = 2
a * c^n - b = 3 * 4 - 10 = 2
二進制中的 2 是執行 010 步:3 - 0 = 3, 3 * 2 = 6, 6 - 1 = 5, 5 * 2 = 10

或者

a = 2, b = 25, c = 6
選擇 n = 2
a * c^n - b = 47
47 基數 6 是 115
執行的步驟:2 - 1 = 1, 1 * 6 = 6, 6 - 1 = 5, 5 * 6 = 30, 30 - 2 - 2 - 1 = 25

在蟒蛇中:

def a_to_b(a, b, c):
    # calculate n
    n = 0
    pow_c = 1
    while a * pow_c - b < 0:
        n += 1
        pow_c *= 1

    # calculate coefficients
    d = a * pow_c - b
    coeff = []
    for i in range(0, n + 1):
        coeff.append(d // pow_c) # calculate x and append to terms
        d %= pow_c               # remainder after eliminating ith term
        pow_c //= c

    # sum up subtractions and multiplications as defined by the coefficients
    return n + sum(c // 2 + c % 2 for c in coeff)  

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM