28. 退治大赛

在一次异变中,早苗酱和灵梦展开退治大赛。假设现在一共有 N 个妖怪,每个妖怪残机数为 A[i]。由早苗酱开始两个人轮流行动。早苗酱每次能伤害至多一个妖怪,将其残机数减一(也可以选择不作为);灵梦每次一定会伤害所有妖怪,全体残机数减一。当有一个妖怪的残机数在某人的回合被减至0或更少时,这个妖怪被退治(消失),同时该人得一分。现在早苗酱想知道自己最多能拿多少分。

输入

输入包含多组测试用例,第一行表示测试用例的个数。

每组测试用例包含两行数据,第一行为 N ( 1 ≤ N ≤ 10^5 ),表示妖怪个数,第二行为 N 个数,表示每个妖怪的残机数 ( 1 ≤ A[i] ≤ 10^8 )。

输出

早苗酱最高可能的得分。

测试输入关于“测试输入”的帮助期待的输出关于“期待的输出”的帮助时间限制关于“时间限制”的帮助内存限制关于“内存限制”的帮助额外进程关于“{$a} 个额外进程”的帮助
测试用例 1以文本方式显示
  1. 2↵
  2. 5↵
  3. 1 2 3 4 5↵
  4. 5↵
  5. 5 5 5 5 5↵
以文本方式显示
  1. 5↵
  2. 2↵
1秒64M0



题解:

如果给的残机数是依次递增的,则早苗酱可以全部拿下。但是样例中会出现重复的残机数,因此早苗酱应该尽力使残机数变为依次递增型的。具体一点来说就是早苗酱将重复的数字减至离当前被减数字最近的且在当前序列中尚未出现的数字处(重复的数字中第一个不用减)。而每减一,所花的次数加一。这种操作完成后,所花掉的次数=减掉的级数+1(因为这个数被移动到相应位置后,空闲位置减少了一个,所以要加一,其实拥有的次数就是空闲位置的数目)。而一开始拥有的次数等于1~max中未出现的数字的种类个数(max为当前输入的最大残机数)。

例子:

8 9 10 10 15 16 20 20 20   一开始拥有的次数14,出现的数字种类个数ans1=6;

第一次移动后:7 8 9 10 15 16 20 20 20 所花的次数=减掉的级数+1=(10-7)+1=4;

第二次移动后:7 8 9 10 15 16 19 20 20 所花的次数=2;

第三次移动后:7 8 9 10 15 16 18 19 20 所花的次数=3;

移动操作完成后,将各次移动所花的次数排序得path= {2,3,4};

而一开始拥有的次数为14,能负担这3个,即2,3,4(因为2+3+4=9<14),故负担数ans2=3,最后结果ans=ans1+ans2=6+3=9,即早苗酱能拿9分。

ans1为必拿的分,因为ans1代表着递增,而递增的肯定能够被拿下,那么接下来就是算重复的残机数中又有多少可以被拿下,即ans2,这个就需要把重复的移动成满足依次递增的,这样才能够拿下,而移动本身是需要消耗次数的,这个次数就是未出现数的种类个数,因为未出现所以能够提供移动的空位置。

具体操作就是输入一串数组,再从小到大排好序,求ans1(这个最好放在算所花次数时一块求,效率比较高),然后就是算所花次数,这其中要记录相邻数字间的差值,像差值为1的就没必要记录,最起码要2及以上,这个可以用结构体数组记录,结构体中一个记录上限,一个记录下限,为什么用数组呢,因为例如上例中1~7是一组,而11~14,17~19又是不同组别,自行体会,然后一边记录一遍算所花次数以及ans1,最后搞定ans2,基本上就没了。一些小细节嘛我也记不太清,也就是些临界条件的处理吧。


#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int MonVol[100005] = { 0 };
int path[5000] = { 0 };
typedef struct node
{
	int Yz;
	int Xz;
}node1;
node1 ChaZhi[5000];
int cmp(const void *a, const void *b)
{
	return *(int*)a > *(int*)b ? 1 : -1;
}
int main()
{
	int count, _num;
	scanf("%d", &count);
	int Sum,sum;
	while (count--)
	{
		int ssum = 1;
		memset(MonVol, 0, sizeof(MonVol));
		memset(path, 0, sizeof(path));
		memset(ChaZhi, 0, sizeof(ChaZhi));
		scanf("%d", &_num);
		for (int i = 0; i < _num; i++)
			scanf("%d", &MonVol[i]);
		qsort(MonVol, _num, sizeof(MonVol[0]), cmp);
		int per1 = 0, per2 = 0;
		if (MonVol[0] == 1)
		{
			ChaZhi[per1].Yz = 0;
			ChaZhi[per1].Xz = 1;
			sum = 0;
		}
		else
		{
			ChaZhi[per1].Yz = MonVol[0] - 1;
			ChaZhi[per1].Xz = 1;
			sum = ChaZhi[per1].Yz - ChaZhi[per1].Xz + 1;
		}
		for (int i = 1; i < _num; i++)
		{
			if (MonVol[i] != MonVol[i - 1])
				ssum++;
			if (MonVol[i] - MonVol[i - 1]>1)
			{
				per1++;
				ChaZhi[per1].Yz = MonVol[i] - 1;
				ChaZhi[per1].Xz = MonVol[i - 1] + 1;
				sum += ChaZhi[per1].Yz - ChaZhi[per1].Xz + 1;
			}
			else if (MonVol[i] - MonVol[i - 1] == 0)
			{
				if (ChaZhi[per1].Yz < ChaZhi[per1].Xz)
				{
					if (per1 == 0)
						goto aa;
					else
						per1--;
				}
				path[per2] = MonVol[i] - ChaZhi[per1].Yz+1;
				ChaZhi[per1].Yz--;
				per2++;
			}
		aa:;
		}
		Sum = ssum;
		qsort(path, per2, sizeof(path[0]), cmp);
		for (int i = 0; path[i] != 0; i++)
		{
			if (sum - path[i] > 0)
			{
				sum -= path[i];
				Sum++;
			}
			else if (sum - path[i] == 0)
			{
				Sum++;
				break;
			}
			else
				break;
		}
		printf("%d\n", Sum);
	}

	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值