小Q的父母要出差N天,走之前給小Q留下了M塊巧克力。小Q決定每天吃的巧克力數量不少於前一天吃的一半,但是他又不想在父母回來之前的某一天沒有巧克力吃,請問他第一天最多能吃多少塊巧克力。
例:input:出差 4 天,7塊奶糖
output:4
對於題中條件,每天吃的巧克力數量不少於前一天吃的一半,也就是後一天大於等於前一天的一半。
- 第n天吃的糖爲奇數,第n+1天吃的糖爲第n天的對半向上取整。 大於前一天的一半。
如:第n天吃了 5 顆,第n+1天吃 (5+1)/2 = 3 顆。()
如:第n天吃了 3 顆,第n+1天吃 (3+1)/2 = 2 顆。() - 第n天吃的糖爲偶數,第n+1天吃的糖爲第n天的對半向下取整。等於前一天的一半。
如:第n天吃了 6 顆,第n+1天吃 (6+1)/2 = 3 顆。()
如:第n天吃了 4 顆,第n+1天吃 (4+1)/2 = 2 顆。()
通過以上分析,我們得到一個計算下一天所吃糖數的公式
其中運用了折半的思想,並且對於每天所吃的糖的數量是一個單調遞減的序列。因此我們可以使用二分查詢設計算法。以上公式是一個遞推公式,只要知道首項,也就是第一天所吃的糖的數量就可以計算出後面若干項。
而題目中**請問他第一天最多能吃多少塊巧克力?**其實是要求我們,最後一天必須把糖吃完 。並且要保證第一天吃到最多。那麼我們可以這樣設計算法:
- 模擬總的奶糖數爲一個數組,如
- 假設第一天吃 塊奶糖,並計算按此方法,到最後一天是否有剩餘的糖。設吃了sum塊(前n項和)
- 情況一:不夠吃,即到最後一天所需奶糖數超過所給奶糖數。
sum > m
- 減少第一天的奶糖數,
- 情況二:夠吃,即到最後吃完還有剩餘。
sum < m
- 增加第一天的奶糖數,
- 情況一:不夠吃,即到最後一天所需奶糖數超過所給奶糖數。
- 最後, 時,即不會有剩餘,也不會不夠吃。即
sum == m
/* 第一天吃num塊,nday天后吃的塊數 */
int Sum(int nday, int num) // 天數 第一天吃的奶糖數
{
int sum = 0;
while (nday--)
{
sum += num;
num = (num + 1) / 2;
}
return sum;
}
/* 貪吃的小Q */
int GreedyLittleQ(int days, int num) // 天數 糖數
{
if (days < 1 || num < 1) return -1;
int left = 1, right = num;
while (left <= right)
{
int mid = (right + left) / 2; // 如果第一天吃mid塊
int sum = Sum(days, mid); // days天后吃的總塊數
bool tag = sum > num; // 糖是否夠吃
if (tag) // 不夠吃
{
right = mid - 1;// 減少第一天的數量
}
else // 夠吃 ==》還有剩餘的糖
{
left = mid + 1;// 適當增加第一天的糖
}
}
return right;
}
int main()
{
int days, num;
while (cin >> days >> num)//, days > 0 && num > 0
{
cout << GreedyLittleQ(days, num) << endl;
}
return 0;
}