洛谷 P1582 倒水

2022-04-07 01:59:47 字數 1672 閱讀 8568

一天,cc買了n個容量可以認為是無限大的瓶子,開始時每個瓶子裡有1公升水。接著~~cc發現瓶子實在太多了,於是他決定保留不超過k個瓶子。每次他選擇兩個當前含水量相同的瓶子,把乙個瓶子的水全部倒進另乙個裡,然後把空瓶丟棄。(不能丟棄有水的瓶子)

顯然在某些情況下cc無法達到目標,比如n=3,k=1。此時cc會重新買一些新的瓶子(新瓶子容量無限,開始時有1公升水),以到達目標。

現在cc想知道,最少需要買多少新瓶子才能達到目標呢?

輸入格式:

一行兩個正整數, n,k(1\le n\le 2\times 10^9,k\le 10001≤n≤2×109,k≤1000)。

輸出格式:

乙個非負整數,表示最少需要買多少新瓶子。

輸入樣例#1: 

3 1

輸出樣例#1: 

1

輸入樣例#2: 

13 2

輸出樣例#2: 

3

輸入樣例#3: 

1000000 5

輸出樣例#3: 

15808

解題思路:

首先明確一點,每個瓶中的水量都是2的冪,這個不難證明。 其次,想要瓶子更少,則要盡可能把瓶子合併,這是什麼意思呢? 舉個例子,輸入n=13,k=2,先不考慮購買新瓶子和k,給出13個瓶子的兩種合併方案,4 4 4 1和8 4 1。不廢話,後者顯然更好。 其實也不難證明上面這條的最優性質,總之,我們總是希望盡可能把多的瓶子合併。 實際演算法不難,首先把瓶子先進行合併,最後總會得到乙個無法再合併的結果,比如上面的8 4 1,但是這時候我們仍有三個瓶子,而資料要求我們最多剩下2個瓶子,所以我們第二步就是對最後兩個瓶子進行合併,這時候直接4-1=3,即所求需要購買的瓶子數,於是最後兩個瓶子可以合併為乙個蓄水量為8的瓶子。

演算法設計也比較簡單,我這裡用的是遞迴來實現。

func1(n,r): 返回將n個瓶子存入陣列f之後,所使用的陣列長度,r傳入1。 我們在這個函式中找到小於n的最大的2的冪y,這個數字填充陣列當前位置,然後再遞迴呼叫func1(n-y,r+1),返回其返回值。 邊界條件為n==0,此時直接返回r-1。

func2(n,r): 合併陣列f中下標為r~n的瓶子,通常r<=n。 兩個邊界條件:1.n

ac**:

1 #include2

#define ll long long

3using

namespace

std;

4 ll n,k,f[1005];5

intprocess1(ll n1,ll r)

12 f[r] =y;

13return process1(n1-y,r+1

); 14}15

intprocess2(ll n2,ll r)

22 ll u = process2(n2,r+1

);23 ll e = f[r] - f[r+1

];24 f[r] *= 2

; 25

return u +e;26}

27int

main()

28

洛谷P1582 倒水

一天,cc買了n個容量可以認為是無限大的瓶子,開始時每個瓶子裡有1公升水。接著 cc發現瓶子實在太多了,於是他決定保留不超過k個瓶子。每次他選擇兩個當前含水量相同的瓶子,把乙個瓶子的水全部倒進另乙個裡,然後把空瓶丟棄。不能丟棄有水的瓶子 顯然在某些情況下cc無法達到目標,比如n 3,k 1。此時cc...

洛谷P1582 倒水

一天,cc買了n個容量可以認為是無限大的瓶子,開始時每個瓶子裡有1公升水。接著 cc發現瓶子實在太多了,於是他決定保留不超過k個瓶子。每次他選擇兩個當前含水量相同的瓶子,把乙個瓶子的水全部倒進另乙個裡,然後把空瓶丟棄。不能丟棄有水的瓶子 顯然在某些情況下cc無法達到目標,比如n 3,k 1。此時cc...

洛谷P1582 倒水

題目描述 這道題經歷了 70 80 90 100的坎坷路程 易得2的次方個瓶子可以倒在一起,所以只需要每一次找出n中最大的2的次方的數,將n減去這個數,k 直到k 1 然後找出比k大的最小2的次方數,用這個數減去k就是答案。然而我只得了70分,後來我想是不是2的31次方是不是不能用int,果然改了l...