金币游戏
Problem Description
在游戏中鲍里斯和瓦西里轮流进行游戏操作,鲍里斯首先进行操作。在每一轮游戏中选手可以选择一个整数x(2x+1<=n),并从第x号箱子到第2x+1号箱子的每个箱子中取走一个金币(如果这个箱子中还有金币的话)。当所有箱子中的金币全被取走后,游戏结束。
鲍里斯不是一个贪婪的守财奴,但鲍里斯是一个懒惰的家伙。因此,他想知道最少几轮游戏操作后可以完成游戏。请你帮助鲍里斯计算一下这个最小值。需要注意的是鲍里斯计数,不仅包括他的操作,他也计算瓦西里的操作。
Input
在每组测试数组的第一行包含一个整数n,表示箱子的个数,其中1<=n<=100。
在每组测试数组的第二行包含n个整数,分别表示游戏开始前每个箱子中金币的个数a[i],其中1<=a[i]<=1000。
Output
Sample Input
2 1 1 3 1 2 3
Sample Output
-1 3
Author
刚开始接触这道题的时候感觉无从下手,不过仔细分析了一下,发现有几点地方要注意:
1.箱子数目一定是大于等于3的,因为1*2+1=3,不然的话一次也不能取,金币永远也取不完了!
2.箱子数目一定不能为偶数,因为如果这样的话,最后一个箱子一定取不到,比如说n = 4,则x最大只能取1,4号箱子一定取不到!
3.要注意题目的关键所在,即对于每只箱子来说至少要取箱子里的金币的数目这么多次数,才能取完该箱子的金币!
我的思路是:将问题一步步缩小,我们发现x=1时,能取到1~3号箱子,x=2时,能取到2~5号箱子,x=3时,能取到3~7号箱子.....
我们发现,每一个x(x>1)相对于前面一个x,这个x总有两个箱子是前面那个x所取不到的,如x=2时,4,5号箱子x=1时取不到,x=3是6,7号箱子x=2取不到,依次类推……
这样的话,我们就发现规律了,如果箱子数为n,假设此时我们能取的最大的数为max,那么n, n - 1,号箱子只有当x取max时才能取到,也就是说,我们至少要取max为n, n - 1号箱子里最大的金币的数目这么多次数,才能将n, n -1号箱子里的金币取完,最重要的是,我们必须要取max这么多次,因为只有取max,我们才能取到n, n - 1号箱子里的金币!
等到n , n - 1号箱子取空之后,我们发现问题的规模变小了n -> n - 2(空箱子可以丢弃了!),然后我们再按照前面的方法再次分析,最终可以将n -> 3,而这种问题已经解答了,即至少要取1, 2, 3号箱子里最多金币数目次数才能取完,至此,题目已经完满解决!
代码如下:
#include <iostream>
const int SIZE = 101;
using namespace std;
int main()
{
int times;
cin >> times;
while (times--)
{
int num, numOfGold[SIZE], tmp, i, j;
int max,min;
cin >> num;
tmp = (num - 1) / 2; /*tmp是能取到的最大的x*/
for (i = 1; i <= num; i++)
cin >> numOfGold[i]; /*输入每个箱子金子数目*/
if (num < 3) /*箱子个数至少要为3个,否则一次也不能取*/
{
cout << "-1" << endl;
}
else
if ((num & 0x0001) == 0) /*如果箱子比能取到的最大的x的2倍+1还多,说明num号箱子一定取不到*/
{
cout << "-1" << endl;
}
else
{
min = 0;
for (i = num; i >= 5; i -= 2) /*一次一次地将问题的规模缩小*/
{
max = numOfGold[i] > numOfGold [i - 1] ? numOfGold[i] : numOfGold[i - 1];
for (j = (i - 1) / 2; j <= i; j++)
{
numOfGold[j] -= max;
if (numOfGold[j] < 0)
numOfGold[j] = 0;
}
min += max;
}
for (i = 1, max = 0; i <= 3; i++) /*如果只有三个箱子,那么取的次数一定是三个箱子内最多金币的数目*/
{
if (numOfGold[i] > max)
max = numOfGold[i];
}
min += max;
cout << min << endl;
}
}
return 0;
}