杭电DIY——金币游戏

31 篇文章 1 订阅

金币游戏

Problem Description

海盗鲍里斯和瓦西里正在玩一个非常有趣的游戏。他们两人共有n个装有金币的箱子,箱子被从1到n进行了编号,第i号箱子中有a[i]枚金币。

在游戏中鲍里斯和瓦西里轮流进行游戏操作,鲍里斯首先进行操作。在每一轮游戏中选手可以选择一个整数x(2x+1<=n),并从第x号箱子到第2x+1号箱子的每个箱子中取走一个金币(如果这个箱子中还有金币的话)。当所有箱子中的金币全被取走后,游戏结束。

鲍里斯不是一个贪婪的守财奴,但鲍里斯是一个懒惰的家伙。因此,他想知道最少几轮游戏操作后可以完成游戏。请你帮助鲍里斯计算一下这个最小值。需要注意的是鲍里斯计数,不仅包括他的操作,他也计算瓦西里的操作。

Input

输入的第一行包含一个整数T,表示测试数据的组数。
在每组测试数组的第一行包含一个整数n,表示箱子的个数,其中1<=n<=100。
在每组测试数组的第二行包含n个整数,分别表示游戏开始前每个箱子中金币的个数a[i],其中1<=a[i]<=1000。

Output

对每组测试数据输出一个整数,表示要结束游戏需进行的最少的游戏轮数。如果游戏无法进行下去(即无x满足2x+1 <= n),或有的箱子中的金币永远无法取到的话,请输出-1。

Sample Input

2
1
1
3
1 2 3

Sample Output

-1
3

Author

guosl

       


    刚开始接触这道题的时候感觉无从下手,不过仔细分析了一下,发现有几点地方要注意:
     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;
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值