第四十一題 (知識遷移能力)和爲S的連續正數序列
題目描述
小明很喜歡數學,有一天他在做數學作業時,要求計算出9~16的和,他馬上就寫出了正確答案是100。但是他並不滿足於此,他在想究竟有多少種連續的正數序列的和爲100(至少包括兩個數)。沒多久,他就得到另一組連續正數和爲100的序列:18,19,20,21,22。現在把問題交給你,你能不能也很快的找出所有和爲S的連續正數序列? Good Luck!
輸出描述:
輸出所有和爲S的連續正數序列。序列內按照從小至大的順序,序列間按照開始數字從小到大的順序
方法一
想了一個比較笨的方法
class Solution:
def FindContinuousSequence(self, tsum):
i = 1
out = []
while i <= tsum//2+1:
sum = 0
t = []
j = i
while sum < tsum:
sum += j
t.append(j)
j += 1
if sum == tsum and len(t)>1:
out.append(t)
i += 1
return out
原以爲會因爲複雜度高提交失敗,沒想到過了。
在牛客的討論區發現兩個比較好的方法
方法二
首先是雙指針法
class Solution:
def FindContinuousSequence(self, tsum):
out = []
i = 1
j = 2
while i < j:
t = []
s = (i+j)*(j-i+1)//2
if s == tsum:
for k in range(i, j+1):
t.append(k)
out.append(t)
if s < tsum:
j += 1
else:
i += 1
return out
方法三
參考‘丁滿歷險記’解析
1)由於我們要找的是和爲S的連續正數序列,因此這個序列是個公差爲1的等差數列,而這個序列的中間值代表了平均值的大小。假設序列長度爲n,那麼這個序列的中間值可以通過(S / n)得到,知道序列的中間值和長度,也就不難求出這段序列了。
2)滿足條件的n分兩種情況:
n爲奇數時,序列中間的數正好是序列的平均值,所以條件爲:(n & 1) == 1 && sum % n == 0;
n爲偶數時,序列中間兩個數的平均值是序列的平均值,而這個平均值的小數部分爲0.5,所以條件爲:(sum % n) * 2 == n.
3)由題可知n >= 2,那麼n的最大值是多少呢?我們完全可以將n從2到S全部遍歷一次,但是大部分遍歷是不必要的。爲了讓n儘可能大,我們讓序列從1開始,
根據等差數列的求和公式:S = (1 + n) * n / 2,得到.
class Solution:
def FindContinuousSequence(self, tsum):
out = []
n = int((2*tsum)**0.5)
for i in range(n, 1,-1):
if (i & 1 == 1 and tsum % i == 0) or ((tsum % i) * 2 == i):
# 求出滿足條件的i
temp = []
k = int((tsum / i) - (i - 1) / 2)
for j in range(i):
temp.append(k)
k += 1
out.append(temp)
return out
這個方法還是要好好理解一下的
第四十二題 (知識遷移能力)和爲S的兩個數字
題目描述
輸入一個遞增排序的數組和一個數字S,在數組中查找兩個數,使得他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。
輸出描述:
對應每個測試案例,輸出兩個數,小的先輸出。
雙指針法
class Solution:
def FindNumbersWithSum(self, array, tsum):
out =[]
if not array:
return out
i = 0
j = len(array)-1
mins = array[j]*array[j]
while i < j:
if array[i]+array[j] == tsum:
temp = array[i]*array[j]
if temp < mins:
mins = temp
out.append(array[i])
out.append(array[j])
i += 1
j -= 1
elif array[i]+array[j] < tsum:
i += 1
elif array[i]+array[j] > tsum:
j -= 1
return out
根據‘馬客(Mark)’說的:找到的第一組(相差最大的)就是乘積最小的。
證明:考慮x+y=C(C是常數),x*y的大小。不妨設y>=x,y-x=d>=0,即y=x+d, 2x+d=C, x=(C-d)/2, x*y=x(x+d)=(C-d)(C+d)/4=(C^2-d^2)/4,也就是x*y是一個關於變量d的二次函數,對稱軸是y軸,開口向下。d是>=0的,d越大, x*y也就越小。
所以其實代碼中設置的mins並沒有什麼用,去掉也不會影響最後的結果。
第四十三題 (知識遷移能力)左旋轉字符串
題目描述
彙編語言中有一種移位指令叫做循環左移(ROL),現在有個簡單的任務,就是用字符串模擬這個指令的運算結果。對於一個給定的字符序列S,請你把其循環左移K位後的序列輸出。例如,字符序列S=”abcXYZdef”,要求輸出循環左移3位後的結果,即“XYZdefabc”。是不是很簡單?OK,搞定它!
使用切片
class Solution:
def LeftRotateString(self, s, n):
if not s:
return ""
n = n %(len(s))
return s[n:]+s[:n]
也可以使用三次反轉
class Solution:
def LeftRotateString(self, s, n):
if not s: return s
s = list(s)
self.reverse(s, 0, n - 1)
self.reverse(s, n, len(s) - 1)
self.reverse(s, 0, len(s) - 1)
return ''.join(s)
def reverse(self, s, start, end):
while start < end:
s[start], s[end] = s[end], s[start]
start += 1
end -= 1
第四十四題 (知識遷移能力)翻轉單詞順序列
題目描述
牛客最近來了一個新員工Fish,每天早晨總是會拿着一本英文雜誌,寫些句子在本子上。同事Cat對Fish寫的內容頗感興趣,有一天他向Fish借來翻看,但卻讀不懂它的意思。例如,“student. a am I”。後來才意識到,這傢伙原來把句子單詞的順序翻轉了,正確的句子應該是“I am a student.”。Cat對一一的翻轉這些單詞順序可不在行,你能幫助他麼?
class Solution:
def ReverseSentence(self, s):
s = list(map(str,s.split(" ")))
s.reverse()
return " ".join(s)
附:推薦工程師死絕的世界
插一句:2019.1.22
今天參加了牛客網的寒假算法訓練營(一),做到自閉,我怕是對自己有什麼誤解。
另外呢,日本IT求職學習服務平臺PAIZA推出了一個編程遊戲《工程師死絕的世界》
玩了幾局感覺可以,推薦一下,看懂全靠Google翻譯。
第四十五題 (抽象建模能力)撲克牌順子
題目描述
LL今天心情特別好,因爲他去買了一副撲克牌,發現裏面居然有2個大王,2個小王(一副牌原本是54張^_^)...他隨機從中抽出了5張牌,想測測自己的手氣,看看能不能抽到順子,如果抽到的話,他決定去買體育彩票,嘿嘿!!“紅心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是順子.....LL不高興了,他想了想,決定大\小 王可以看成任何數字,並且A看作1,J爲11,Q爲12,K爲13。上面的5張牌就可以變成“1,2,3,4,5”(大小王分別看作2和4),“So Lucky!”。LL決定去買體育彩票啦。 現在,要求你使用這幅牌模擬上面的過程,然後告訴我們LL的運氣如何, 如果牌能組成順子就輸出true,否則就輸出false。爲了方便起見,你可以認爲大小王是0。
思路:數字0可以代表任意數
有以下幾個條件必須滿足
1、除0以外,不能有其他重複數字,注意0最多隻有4個。
2、滿足順子的話,排序後前一個數比後一個數小1。同時需要考慮有0的情況,如果後面一個數比前面一個數大於1以上,那麼就用0來補。
class Solution:
def IsContinuous(self, numbers):
if not numbers:
return None
zero = numbers.count(0)
if zero == 5:
return False
numbers.sort()
for i in range(0, len(numbers)-1):
if numbers[i] != 0:
if numbers[i] == numbers[i+1]:
return False
zero -= numbers[i+1] - numbers[i] - 1
if zero < 0:
return False
return True
第四十六題 (N)(抽象建模能力)孩子們的遊戲(圓圈中最後剩下的數)
題目描述
每年六一兒童節,牛客都會準備一些小禮物去看望孤兒院的小朋友,今年亦是如此。HF作爲牛客的資深元老,自然也準備了一些小遊戲。其中,有個遊戲是這樣的:首先,讓小朋友們圍成一個大圈。然後,他隨機指定一個數m,讓編號爲0的小朋友開始報數。每次喊到m-1的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續0...m-1報數....這樣下去....直到剩下最後一個小朋友,可以不用表演,並且拿到牛客名貴的“名偵探柯南”典藏版(名額有限哦!!^_^)。請你試着想下,哪個小朋友會得到這份禮品呢?(注:小朋友的編號是從0到n-1)
看着挺簡單的,但是不知道怎麼用算法表示出來
class Solution:
def LastRemaining_Solution(self, n, m):
if n < 1:
return -1
if n == 1:
return 0
l = []
for i in range(n):
l.append(i)
i = -1
j = 0
while l:
k = (j+m-1)%n
i = l.pop(k)
n -= 1
j = k
return i
參考:約瑟夫環問題
時間複雜度爲O(n),空間複雜度爲O(1)
在這n個數字中,第一個被刪除的數字是(m-1)%n,爲簡單起見記爲k。那麼刪除k之後的剩下n-1的數字爲0,1,…,k-1,k+1,…,n-1,並且下一個開始計數的數字是k+1。相當於在剩下的序列中,k+1排到最前面,從而形成序列k+1,…,n-1,0,…k-1。該序列最後剩下的數字也應該是關於n和m的函數。由於這個序列的規律和前面最初的序列不一樣(最初的序列是從0開始的連續序列),因此該函數不同於前面函數,記爲f’(n-1,m)。最初序列最後剩下的數字f(n,m)一定是剩下序列的最後剩下數字f’(n-1,m),所以f(n,m)=f’(n-1,m)。
接下來我們把剩下的的這n-1個數字的序列k+1,…,n-1,0,…k-1作一個映射,映射的結果是形成一個從0到n-2的序列:
k+1 -> 0
k+2 -> 1
…
n-1 -> n-k-2
0 -> n-k-1
…
k-1 -> n-2
把映射定義爲p,則p(x)= (x-k-1)%n,即如果映射前的數字是x,則映射後的數字是(x-k-1)%n。對應的逆映射是p-1(x)=(x+k+1)%n。
由於映射之後的序列和最初的序列有同樣的形式,都是從0開始的連續序列,因此仍然可以用函數f來表示,記爲f(n-1,m)。根據我們的映射規則,映射之前的序列最後剩下的數字f’(n-1,m)= p-1 [f(n-1,m)]=[f(n-1,m)+k+1]%n。把k=(m-1)%n代入得到f(n,m)=f’(n-1,m)=[f(n-1,m)+m]%n。
經過上面複雜的分析,我們終於找到一個遞歸的公式:f(n,m)= [f(n-1,m)+m]%n
19.1.23 說實話沒怎麼看明白,先這樣,等明天腦子清醒了再說。
第四十七題 (發散思維能力)求1+2+3+...+n
題目描述
求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。
class Solution:
def Sum_Solution(self, n):
return (1+n)*n/2
第四十八題(N) (發散思維能力)不用加減乘除做加法
題目描述
寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。
參考牛客討論
明天補充
我的自言自語:第一個想法是換成二進制,然後用最原始的方法做,用i,j分別表示兩個數的同一位,t表示進位
i=0 and j = 0 看進位爲0還是1來確定相加後爲0還是1
i= 1 and j = 1 看進位爲0還是1來確定相加後爲1還是0,同時進位t=1
剩下的一樣
太複雜了,只實現了對正數的相加。
插一句:2019.1.24
今天參加了牛客網的寒假算法訓練營(二),還是兩道題告終