【筆試題】 二分查詢 | 貪吃的小Q ——M塊巧克力,每天吃的巧克力數量不少於前一天吃的一半 ...

小Q的父母要出差N天,走之前給小Q留下了M塊巧克力。小Q決定每天吃的巧克力數量不少於前一天吃的一半,但是他又不想在父母回來之前的某一天沒有巧克力吃,請問他第一天最多能吃多少塊巧克力。

例:input:出差 4 天,7塊奶糖
  output:4

對於題中條件,每天吃的巧克力數量不少於前一天吃的一半,也就是後一天大於等於前一天的一半

  • 第n天吃的糖爲奇數,第n+1天吃的糖爲第n天的對半向上取整大於前一天的一半。
    如:第n天吃了 5 顆,第n+1天吃 (5+1)/2 = 3 顆。([2.5]=3[2.5]^{上} = 3
    如:第n天吃了 3 顆,第n+1天吃 (3+1)/2 = 2 顆。([1.5]=2[1.5]^{上} = 2
  • 第n天吃的糖爲偶數,第n+1天吃的糖爲第n天的對半向下取整等於前一天的一半。
    如:第n天吃了 6 顆,第n+1天吃 (6+1)/2 = 3 顆。([3.5]=3[3.5]_{下} = 3
    如:第n天吃了 4 顆,第n+1天吃 (4+1)/2 = 2 顆。([2.5]=2[2.5]_{下} = 2
通過以上分析,我們得到一個計算下一天所吃糖數的公式 mid=(right+left)/2mid = (right+left)/2

其中運用了折半的思想,並且對於每天所吃的糖的數量是一個單調遞減的序列。因此我們可以使用二分查詢設計算法。以上公式是一個遞推公式,只要知道首項,也就是第一天所吃的糖的數量就可以計算出後面若干項。

而題目中**請問他第一天最多能吃多少塊巧克力?**其實是要求我們,最後一天必須把糖吃完 。並且要保證第一天吃到最多。那麼我們可以這樣設計算法:

  • 模擬總的奶糖數爲一個數組,如 ar[m]=1,2,3,4,5,6,7,8,9,... ...,mar[m] = {1,2,3,4,5,6,7,8,9, ... \ ..., m}
  • 假設第一天吃 (m+1)/2(m+1)/2 塊奶糖,並計算按此方法,到最後一天是否有剩餘的糖。設吃了sum塊(前n項和)
    • 情況一:不夠吃,即到最後一天所需奶糖數超過所給奶糖數。sum > m
      • 減少第一天的奶糖數,right=mid1;right = mid-1;
    • 情況二:夠吃,即到最後吃完還有剩餘。 sum < m
      • 增加第一天的奶糖數,left=mid+1;left = mid+1;
  • 最後,left=rightleft = right 時,即不會有剩餘,也不會不夠吃。即 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章