問題描述:
任意乙個整數x都可以寫成一系列2的冪的組合,這裡的組合是指用加減法把它們接連起來。令f(x)為x需要的最少的2的冪的項數,求f(x)。如:7
=20+
21+2
2 7=
23−2
0 ∴
f(7)
=2宣告:因為f
(x)=
f(−x
) , 所以後文一致假設x非負。另外令f(
0)=0
。下面介紹三種演算法,由易到難,且演算法之間存在聯絡。
演算法1:
令floor2(x)表示不大於x的最大2冪, ceil2(x)表示不小於x的最小2冪,最優的組合要麼是floor2(x)加上其它一些項,或者是ceil2(x)減去其它一些項。
於是可以得到求f(x)的乙個遞迴過程如下:
def
f(x):
if x == 0: return
0 l = floor2(x)
u = ceil2(x)
if l == u: return
1return
1 + min(f(x - l), f(u - x))
演算法2:
觀察演算法1會發現,有很多的計算都是重複的
如對於對於求f(
測試**:
def
floor2
( x ):
a = 1
while a < x:
a = a * 2
if a == x:
return a
else:
return a / 2
defceil2
( x ):
a = 1
while a < x:
a = a * 2
return a
deff
(x):
print x
if x == 0:
return
0 l = floor2(x)
u = ceil2(x)
if l == u:
return
1return
1 + min(f(x - l), f(u - x))
print f( 23 )
因此可以使用乙個陣列儲存已經計算過的結果:
def
f(x):
if x == 0: return
0if x in table: return table[x]
l = floor2(x)
u = ceil2(x)
if l == u: return
1 r = 1 + min(f(x - l), f(u - x))
table[x] = r
return r
演算法3:
演算法2是乙個從本身串層層向下遞推的過程,因此需要額外記憶體儲存結果,因此想到,可以改進演算法2為由下向上的過程。
分析演算法2的遞推過程:如x
=(1010110)2
x−floo
r2(x
)=(0010110)2
ceil2(x
)−x=
(10000000)2
−(1010110)2
=(0101010)2
= ~x+
1 我們會發現,x−
floo
r2(x
) 表示的是自身串從左往右數,第二個1之後的子串,ce
il2(
x)−x
表示的是自身串從左往右數,第乙個0之後的子串取反加1
因此,演算法3的思路就是,從右往左遍歷自身串,用u表示以1開頭的串的f(
x)值,用d表示以0開頭的串取反加一的f(
x)值從右往左掃,掃到1則更新u,掃到0則更新d
自身串最右邊剛開始的那些0是沒有用的,一直掃到1開始更新,u、d賦初值1
**中的》1不要當成除以2,而是看做二進位制字串的右移。&1 == 1表示最後一位是1.
def
f(x):
if x == 0: return
0while x & 1 == 0: x = x >> 1
u = 1
d = 1
x = x >> 1
while x > 0:
if x & 1 == 1:
u = min(u, d) + 1
else:
d = min(u, d) + 1
x = x >> 1
return u
2023年9月6日更新
今天重新看了這篇文章,發現自己竟然看不懂了,what?現在終於又懂了,再把思路重新梳理一下。
前兩個方法沒什麼好說的,關鍵是第三個方法,裡面的u與v。
u指的是當前二進位制字串(以1開頭)從左往右第二個1開頭的新串的f(
x)值,v指的是當前二進位制字串從左往右第乙個0開頭的新串取反加一得到的新新串的f(
x)值(f(
x)中的x指的是新串與新新串)
舉個例子就懂了,這個方法確實很神奇
1010110
初始,
u: 10: 1
v: 10: 1
ps:10表示代表的串,1表示需要二進位制數的個數
計算110,因為1開頭,所以更新u u=
u+1 : 100 + u 或u
=v+1
: 1000 - v
結果:u: 110: 2
計算1010(原串:0110),更新v v=
u+1 : 10000 - u 或v
=v+1
: 1000 + v
結果:v: 1010: 2
計算10110,更新u u=
u+1 : 10000 + u 或u
=v+1
: 100000 - v
結果:u: 10110: 3
計算101010(原串:010110),更新v v=
u+1 : 1000000 - u 或v
=v+1
: 100000 + v
以此類推
整數拆分問題
問題 對於1個正整數n,將其拆分成幾個正整數的和,如何拆分可使得其乘積最大?csdn使用者pathuang68給出的結論是 如果不在乎是否為整數的話,那麼把每份平均分為e 2.71828459045.時,所得到的乘積是最大的,如果要是整數的話,那麼就選盡可能地靠近e的整數即可,比如3。具體證明請參見...
硬幣拆分問題。
include using namespace std int main else cout x 2 y 1 endl return 0 1.這個硬幣拆分源自hdoj1085題,有1 2 5,3種硬幣,x,y,z分別為其個數,首先x 0時,最小的即為1,其次x不等於0時,要找出5以內有個臨界點,當x...
快速冪問題
所謂的快速冪,實際上是快速冪取模的縮寫 首先,最基本的辦法是 int ans 1 for int i 1 i b i ans ans c ans是對answer的縮寫但是如果a很大,那麼a b的結果就容易非常大,所以在求之前可以先對a做乙個變化 如下 int ans 1 a a c 加上這一句 fo...