3種解法 兩水壺拼水問題

2021-10-04 05:52:12 字數 3827 閱讀 1776

有兩個容量分別為 x公升 和 y公升 的水壺以及無限多的水。請判斷能否通過使用這兩個水壺,從而可以得到恰好 z公升 的水?

如果可以,最後請用以上水壺中的一或兩個來盛放取得的 z公升 水。

你允許:

示例 1: (from the famous 「die hard」 example)

輸入: x = 3, y = 5, z = 4

輸出: true

示例 2:

輸入: x = 2, y = 6, z = 5

輸出: false

思路:目標容量是通過當前兩個瓶子相互減,通過遞迴相減遍歷出所有可能組合的,沒去探尋規律,因此稱為暴力法,不過其搜尋的方式也可以稱為深度優先搜尋。如果遞迴找到返回true,如果遍歷所有可能後沒找到,則返回false

如果目標大於兩個瓶子容量和或目標為0,特殊處理

如果目標大於兩個瓶子容量,則減去大容量作為新目標容量,即原目標容量是新目標容量加上大容量

遞迴裡面不斷通過x和y對當前差值進行處理

遞迴的寫法,在測試用例數值比較大的時候會超出遞迴深度,因此最前面兩行是修改了python預設的遞迴深度。(如果不修改深度,超出深度的測試用例為:104597,104623,123)。

# author: [email protected]

import sys

sys.setrecursionlimit(

1000000

)class

solution

:def

dein

(self,s,x,y,z,t)

:if z == t:

# 找到

return

true

if s.__contains__(t)

:# 已經出現過,則不再遞迴

return

false

s.add(t)

return self.dein(s,x,y,z,

abs(x-t)

)or self.dein(s,x,y,z,

abs(y-t)

)def

canmeasurewater

(self, x:

int, y:

int, z:

int)

->

bool

:if z > x + y:

return

false

if z in(0

,x,y)

:#簡單特例

return

true

if z >

max(x,y)

: z = z -

max(x,y)

s =set(

)return self.dein(s,x,y,z,

max(x,y)

-min

(x,y)

)

思路:由於每次倒水操作都是全部倒掉或者倒滿,都可以看做是對x,y的整數次組合操作,即使用乙個二元一次方程進行求解判斷,將題目抽象為數學問題,可以理解為:

已知x,y,z三個正整數

判斷是否存在兩個整數a,b,使得線性方程可以滿足:ax+

by=z

ax + by = z

ax+by=

z,即在以ab為變數構成的二維座標系中,直線方程是否經過整數座標

a,b的約束條件為:不能同時大於等於1(同時等於1除外),即要檢查的座標點不考慮第一象限中(1,b)(a,1)的那一部分

在數論中有個貝祖定理,是這麼說的:

若x,y是整數,且x和y的最大公約數為k,一定存在整數a和b,使得ax+

by=k

ax+by=k

ax+by=

k成立進一步,等式ax+

by=z

ax+by=z

ax+by=

z有解的充要條件是:z是k的整數倍

另外,等式有解時必然有無窮多個整數解

從貝祖定理知道,本題目可以轉換為判斷,z是否能夠整除x和y的最大公約數。

ab的約束條件首先剔除(思路中的第3點)

輾轉相除得到最大公約數

判斷是否能整除最大公約數

class

solution

:# author: [email protected]

defcanmeasurewater

(self, x:

int, y:

int, z:

int)

->

bool

:if z > x + y:

#剔除約束條件

return

false

if z in(0

,x,y)

:#簡單特例

return

true

x, y =

max(x,y)

,min

(x,y)

while y >0:

#輾轉相除 得到 最大公約數

y, x = x % y, y

return z % x ==

0

思路:每次把每種可能的操作都執行一遍,如果滿足條件則返回true,否則每次把操作結果存入棧,每次取棧頂元素後持續查詢,直到將每次操作的可能性都遍歷完,本演算法使用棧來達到深度優先搜尋演算法,也可以使用佇列來達到廣度優先搜尋演算法。

每次操作最多有以下6種操作方式:

把x倒滿

把y倒滿

把x倒空

把y倒空

把x倒入y,可能把x倒空,也可能把y倒滿

把y倒入x,可能把y倒空,也可能把x倒滿

為了減少重複遍歷,引入visited集合進行過濾來判斷,這種方式跟解法一類似,也可以理解成暴力法,即基於具體規則的暴力破解。本解法使用迴圈來代替遞迴,用於避免超出python的預設最大遞迴深度,當然也可以類似解法一使用遞迴的方式實現。

class

solution

:# author: [email protected]

defcanmeasurewater

(self, x:

int, y:

int, z:

int)

->

bool

: stack =[(

0,0)

] visited =

set(

)while stack:

cx,cy = stack.pop(

)if z in

(cx,cy,cx+cy)

:return

true

if(cx,cy)

in visited:

continue

visited.add(

(cx,cy)

)#加入標籤減少重複遍歷

(x,cy)

)#1. 把x倒滿

(cx,y)

)#2. 把y倒滿(0

,cy)

)#3. 把x倒空

(cx,0)

)#4. 把y倒空

(cx-

min(cx,y-cy)

,cy+

min(y-cy,cx)))

#5. 把x倒入y,可能把x倒空,也可能把y倒滿

(cx+

min(x-cx,cy)

,cy-

min(cy,x-cx)))

#6. 把y倒入x,可能把y倒空,也可能把x倒滿

return

false

八皇后問題的兩種解法

八皇后問題,是回溯演算法 的典型案例。該問題是國際西洋棋棋手馬克斯 貝瑟爾於1848年提出 在8x8格的西洋棋 上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行 同一列或同一斜線上,問有多少種擺法。高斯認為有76種方案。1854年在柏林 的象棋雜誌上不同的作者發表了40種不同的解,後...

6 2分魚問題兩種解法

分魚問題,從e開始遞推,使用for迴圈簡化中間計算,優化列舉 include using namespace std int main if i 0 已找到答案 break num 0 num 1 4 5 1 for int i 0 i 5 i cout num i return 0 分魚問題,從e...

約瑟夫環問題的兩種解法(詳解)

題目 josephus有過的故事 39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死也不要被敵人抓。於是決定了自殺方式,41個人排成乙個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺。然後下乙個重新報數,直到所有人都自殺身亡為止。然而josephus 和他的朋友並...