一
头条的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
输出
2
public class 头条校招 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
int n = sc.nextInt();
int a[] = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
Arrays.sort(a);
int t = 1;
int cnt = 0;
for(int i = 1; i < n; i++) {
if(t < 3) {
if(a[i] - a[i - 1] <= 10) {//如果在10以内,加入正常流程
t++;
//不在10以内
}else if(t == 1 && a[i] - a[i - 1] <= 20) {//当做第三个数使用,补充第二个
cnt++;
t=3;
}else if(t == 2) {//补充第三个
cnt++;
t = 1;
}else {//补充后两个
cnt += 2;
t = 1;
}
}else {//新取三个数比较
t = 1;
}
}
cnt += 3 - t;//到最后可能取不满三个数
System.out.println(cnt);
}
sc.close();
}
}
二、
给两个整数(int)数组,输出相同的元素,时间复杂度限定为O(N)。
输入样例
5 4
11 15 9 12 3
1 8 3 9
输出样例
3 9
public class 两数组找相同元素{
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
int m=sc.nextInt();
Set<Integer> a = new HashSet<>();
for (int i = 0; i < n; i++) {
a.add(sc.nextInt());
}
for (int i = 0; i < m; i++) {
int tmp = sc.nextInt();
if(!a.add(tmp)){//当set中有该元素时,会返回false,否则返回true
System.out.print(tmp+" ");
}
}
sc.close();
}
}
三、
按数组的形式给出函数f(x)的取值,即数组A的A[0]元素为f(0)的取值,数组的取值都为整数,
函数在每个点都是严格单调递增或者严格递减(即A[i-1] != A[i] != A[i+1]),
要求找出最宽的先上升后下降的区间(这个区间内函数的值必须先上升到一个点然后下降,区间的上升段和下降段长度必须都大于0)。
1. 如果找到符合条件的最大区间输出数组对应的左右下标(保证只有一个最大区间)
2. 找不到那么输出-1 -1
输入格式
n
n长度的整数数组
输出格式
区间的范围
输入样例
10
1 3 1 2 5 4 3 1 9 10
输出样例
2 7
数据规模
对于 100% 的数据,1 <=n <=10, 000, 000
public class 找出函数的最宽尖峰 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int a[] = new int[n];
for(int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
sc.close();
int l[] = new int[n];
int r[] = new int[n];
for(int i = 1; i < n; i ++) {
if(a[i] > a[i - 1]) {
l[i] = l[i - 1] + 1;
}
}
for(int i = n - 2; i >= 0; i--) {
if(a[i] > a[i + 1]) {
r[i] = r[i + 1] + 1;
}
}
int max = 0, left = -1, right = -1;
for(int i = 0; i < n; i++) {
//l[i]>0表示上升段必须大于0,r[i]>0表示下降段必须大于0
if(l[i] > 0 && r[i] > 0 && l[i] + r[i] > max) {
max = l[i] + r[i];//max为区间大小
left = i - l[i];//l[i]为从左到右第几个连续的上升点,因此是i-l[i]
right = i + r[i];//r[i]为从右到左第几个连续的下降点,因此事i+r[i]
}
}
System.out.println(left+" "+right);
}
}
四、
给定整数n和m, 将1到n的这n个整数按字典序排列之后, 求其中的第m个数。
对于n=11, m=4, 按字典序排列依次为1, 10, 11, 2, 3, 4, 5, 6, 7, 8, 9, 因此第4个数是2.
输入描述:
输入仅包含两个整数n和m。
数据范围:
对于20%的数据, 1 <= m <= n <= 5 ;
对于80%的数据, 1 <= m <= n <= 10^7 ;
对于100%的数据, 1 <= m <= n <= 10^18.
输出描述:
输出仅包括一行, 即所求排列中的第m个数字.
示例1
输入
11 4
输出
2
public class 数字字典序 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
long n = sc.nextLong();
long m = sc.nextLong();
long res= 1;
m--;//当m为0时,答案即为res
while (m != 0) {
long cnt = getPre(n, res);
if(m >= cnt) {//该前缀不为答案前缀
res++;
m -= cnt;
} else {//该前缀为答案前缀
res *= 10;
m--;
}
}
System.out.println(res);
}
sc.close();
}
private static long getPre(long n, long res) {//n内以pre为前缀的数的个数
long cnt = 0;
long tmp = res + 1;
while(res <= n) {
//如果res是n的前缀时,会选到n+1,其他时候都是1+10+100+...
//n+1是因为从XXX0~n
cnt += Math.min(n + 1, tmp) - res;
res *= 10;
tmp *= 10;
}
return cnt;
}
}
五、
给定整数m以及n各数字A1,A2,..An,将数列A中所有元素两两异或,共能得到n(n-1)/2个结果,请求出这些结果中大于m的有多少个。
输入描述:
第一行包含两个整数n,m.
第二行给出n个整数A1,A2,…,An。
数据范围
对于30%的数据,1 <= n, m <= 1000
对于100%的数据,1 <= n, m, Ai <= 10^5
输出描述:
输出仅包括一行,即所求的答案
示例1
输入
3 10
6 5 10
输出
2
class Trie {
Trie[] next = new Trie[2];
int count = 1;
}
public class 异或 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()){
int n = sc.nextInt();
int m = sc.nextInt();
int a[] = new int[n];
/* 数据太大,用字典树,高位到低位排插入
* 如果两个数异或结果在某高位为1,而m的对应位为0,则肯定任何这两位异或结果为1的都会比m大。
* 高位异或为1, 而m为0的数的个数,相加在除以2既是最终的结果。
*/
Trie root = new Trie();//根节点不存放数字
for (int i = 0; i < n; i++) {
Trie current = root;
a[i] = sc.nextInt();
insert(a[i], current);
}
long result = 0;
for (int i = 0; i < a.length; i++) {
result += query(root, a[i], m, 31);
}
System.out.println(result / 2);
}
sc.close();
}
private static void insert(int a, Trie current) {
for (int j = 31; j >= 0; j--) {//先存高位,因此如果高位没数,则前导会有很多0
int digit = (a >> j) & 1;
if(current.next[digit] == null) {
current.next[digit] = new Trie();
} else {
current.next[digit].count ++;
}
current = current.next[digit];
}
}
private static long query(Trie current, int a, int m, int k) {//a为此时比较的数,m为要比较的数,k为比较的位
if(current == null) { //遍历到叶节点则退出
return 0;
}
int aDigit = (a >> k) & 1;
int mDigit = (m >> k) & 1;
//m为1,异或结果为0不理会,异或结果为1则需要比较更低位
if(aDigit == 1 && mDigit == 1) {
return query(current.next[0], a, m, k - 1);
} else if (aDigit == 0 && mDigit == 1) {
return query(current.next[1], a, m, k - 1);
//m为0,异或结果为0,递归获得结果;异或结果为1,所有分支异或都大于m
} else if (aDigit == 1 && mDigit == 0) {
long p = query(current.next[1], a, m, k - 1);
long q = current.next[0] == null ? 0 : current.next[0].count;
return p + q;
} else if (aDigit == 0 && mDigit == 0) {
long p = query(current.next[0], a, m, k - 1);
long q = current.next[1] == null ? 0 : current.next[1].count;
return p + q;
}
return 0;
}
}