头条–头条校招
一、题目表述
头条的2017校招开始了!为了这次校招,我们组织了一个规模宏大的出题团队,每个出题人都出了一些有趣的题目,而我们现在想把这些题目组合成若干场考试出来,在选题之前,我们对题目进行了盲审,并定出了每道题的难度系统。一场考试包含3道
开放性题目,假设他们的难度从小到大分别为a,b,c,我们希望这3道题能满足下列条件
:
a<=b<=c
b-a<=10
c-b<=10
所有出题人一共出了n道
开放性题目。现在我们想把这n道题分布到若干场考试
中(1场或多场,每道题都必须使用且只能用一次
),然而由于上述条件的限制,可能有一些考试没法凑够3道题
,因此出题人就需要多出一些适当难度的题目来让每场考试都达到要求,然而我们出题已经出得很累了,你能计算出我们最少还需要再出几道题让每场考试都达到要求
吗?
- 输入描述:
输入的第一行包含一个整数n,表示目前已经出好的题目数量。
第二行给出每道题目的难度系数d1,d2,...,dn。
数据范围
对于30%的数据,1 ≤ n,di ≤ 5;
对于100%的数据,1 ≤ n ≤ 10^5,1 ≤ di ≤ 100。
在样例中,一种可行的方案是添加2个难度分别为20和50的题目,这样可以组合成两场考试:
(20 20 23)和(35,40,50)。
-
输出描述:
输出只包括一行,即所求的答案。
输入例子1:
4
20 35 23 40
输出例子1:
2
二、分析
这道题可能读完题后有点唬人,但是只要分析完所有情况并不难
- 对于第一个条件,
a<=b<=c
,为了是前后3个数字满足条件,我们只需要排序
即可 - 对于第二、三个条件,就需要分多种情况讨论:(定义一个指针用来循环判断)
第1种情况
: 这三个都满足要求,即:b - a <= 10 && c - b <= 10,下标指针直接+3,跳过满足情况的数字
;
如:
10,11,20,23....当指针在10的位置上判断10,11,20这3个满足情况,直接跳到23位置继续向后判断
第2种情况
: 第二个比第一个大超过20,不满足b - a <= 10, 那么第一个后面就需要添加2个数才可以,下标指针+1向下判断
如:1,100,120…;当下标指针在1时,判断100 - 1 > 20,
因为前后两个数之间允许的最大差值为10,这里超过20,所以肯定是要添加数字的
。
那么如果只添加一个数字构成1,x,100
,无论添加什么,都不能满足所有情况,1和100之间无论添加谁,都不可以使1,x,100满足任意的b - a <= 100.
所以只能添加2个构成1,x,y使其满足情况
,同时下标指针向后移动到100,继续向下判断
第3种情况
:第二个比第一个大超过10但不大于20,中间加1个数字即可, 下标指针+2
这种情况是上面一种情况的子问题,上面明白了,这种情况理解起来就简单
如:1,15,20,25…;当下标指针在1时,发现15 - 1 >= 10,不满足情况,那么就需要在1和15之间添加数字
。
同样相邻的两个数字之间只允许最大差值为10
,发现添加一个数字可以满足情况,使其构成1,x,15,题目要求是最少情况,这里能添加一个就不考虑添加2个的情况,直接把下标指针移动到20位置继续判断即可
第4种情况
:第一第二满足(即前两个数字满足情况), 第三个第二个不满足(后两个不满足情况), 这种情况和上面有点像,第二个后面添加1个数字, 下标指针+2
如:10,15,26/99999…;当下标指针在10时,
发现10和15满足情况,15和26/9999(26/9999代表无论是26这种情况还是999这种情况,即不管是否大于20的任意不满足的情况)
,那么同样需要添加数字。
同样为了使添加的数字最少,无论后2个数字是什么原因导致不满足情况,只需要在15后面添加一个数字构成10,15,x即可,不用添加2个
到这里这道题大方向就分析完了,发现并不难,但是还有很多小细节需要判断!
三、代码
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 3;
int a[maxn];
int main()
{
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
//先排序使所有数字满足第一个条件
sort(a, a + n);
//特殊情况,直接返回即可
if(n == 1)
printf("%d\n", 2);
else if(n == 2)
{
if(a[1] - a[0] > 20)
printf("%d\n", 4);
//1,1000,只能添加4个构成1,x1,x2和y1,y2,1000
else
//说明小于20大于10;即1,15只需要添加一个就可以满足情况
printf("%d\n", 1);
}
else
{
//遍历的下标指针
int l = 0;
//记录结果
int add = 0;
//因为在当前位置往后判断2个位置了,所以之一范围
while(l < n - 2)
{
//分析中的第一种情况
if(a[l + 1] - a[l] <= 10 && a[l + 2] - a[l + 1] <= 10)
l += 3;
//分析中的第二种情况
else if(a[l + 1] - a[l] > 20)
{
add += 2;
l += 1;
}
//分析中的第三种情况
else if(a[l + 1] - a[l] <= 20 && a[l + 1] - a[l] > 10)
{
add++;
l += 2;
}
//分析中的第四种情况
else if(a[l + 1] - a[l] <= 10 && a[l + 2] - a[l + 1] > 10)
{
add++;
l += 2;
}
//如1,2,3,4这种情况,在判断1时发现满足情况,指针直接+3,发现3 < 2
//不成立,但是不能直接推出,因为还有4没构成3个,所以直接+2即可
if(l == n - 1)
add += 2;
//这种情况同理;1,2,3,4,9999这种情况,在下标指针指向4时,发现3 < 3
//不成立,也不能直接推出,只需要+4构成两队即可
if(l == n - 2)
{
if(a[l + 1] - a[l] > 20)
add += 4;
else
add += 1;
}
}
printf("%d\n", add);
}
}
这种方法比较容易想到,但是并不能完全想到所有情况,毕竟笔试时并没有特殊样例
-
其实这道题在忽悠大家
,题目描述了一大堆都没用,仔细想一下,好像是只要出题数目能被3整除就满足所有条件,不能被3整除补足就行了。 - 出题者满足要求就行了,只要能总题量能被3整除就满足考场需求,至于其他要求我们不用管,我们只要算数量。连排序都不需要
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
cin>>n;
vector<int> a(n);
for(int i = 0; i<n; i++)
{
cin>>a[i];
}
int x = a.size() % 3;
if (x == 0)
cout<<0;
else
cout<<3-x;
return 0;
}
当然我们不要一直依赖于第二种做法,毕竟那不一定是面试官的目的!!!!