最近又参与了一场华为的软件开发实习岗的笔试,于是想将题目分享一下~
如果有啥更好的想法欢迎交流~
Here We Go!
题目一
幼儿园老师安排小朋友做游戏,现在需要给 N 个小朋友进行分组,老师让每个同学写一个名字,代表这位小朋友想和谁分一组。请问老师现在满足所有小朋友意愿的情况下,最多可以将班级分成多少组?
输入描述
第一行输入N,0 < N ≤ 10000
接下来是N行代表每个小朋友希望和谁分到一组,如"John Jack",代表John希望和Jack分到一组,两个名字之间以空格分割,名字本身不存在空格。
输出描述
分组的最多数量
示例输入
6
Jack Tom
Alice John
Jessica Leonie
Tom Alice
John Jack
Leonia Jessica
示例输出
2
示例说明
Jack、Tom、Alice、John分为一组,Jessica和Leonie一组,一共两组。
思路
考察的是并查集的知识。
假设每个小朋友是一个节点(图的节点),那么意愿的双方节点之间连接一条线。那么最后同属一个图中的节点(小朋友)可分为一组。
最后计算一共有多少个图即可。
并查集的思想是 通过标记确定该顶点所在的组。
所以对于一个n个小朋友,n条边的图,我们需要新建一个长度为n的数组f,f[n]代表点n的小朋友团队的“代表人”,当两个点所在团伙“代表人”相同,则这两个点所在小朋友则是相同的。
初始化数组f的时候使其f[i] = i,接着假如1号小朋友想和3号小朋友组队,则不妨令f[3] = f[0];那么这里我们默认下标最小的小朋友为团队的“代表人”,同时“代表人”必须有f[x] = x。
示例代码
wait~
题目二
给定 N 个任务(1≤N≤100) ,任务编号从0开始顺序累加,这N个任务在系统中排队顺序执行,每个任务的自身执行时间为非负整数,依次为t1,12…tn。
部分任务之间存在依赖关系的,某任务所依赖的任务如果没有执行,则该任务需要重回队尾重新排队。只有任务执行以及任务排队等待会消耗时间,其余操作消耗时间忽略不计。
请计算每个任务的实际执行时间(实际执行时间=任务自身执行的时间+在队列中等待其他任务执行的时间)
输入描述
第一行输入按照任务编号递增的顺序表示N个任务的自身执行时间,为逗号分隔的字符串,执行时间取值范围[1,999]。例如:1,3,4 (逗号前后没有空格),表示一共3个任务,每个任务的自身执行时间分别为1,3,4。
第二行输入表示任务之间的依赖关系,为逗号分隔的字符串,每个依赖关系都表明了两个任务编号之间的依赖关系,例如:0->2,表示0号任务依赖于2号任务。
输出描述
按照任务编号递增的顺序,输出每个任务的实际执行时间,以逗号分隔,逗号前后不要空格,例如:8,3,7
示例输入
1, 3, 4
0->2
示例输出
8, 3, 7
思路
维护一个哈希映射HashMap,其中存放的是依赖关系。
那么每遍历到一个任务,会存在一下两种情况,分别处理:
- 无依赖关系:此时可以 前面任务执行的时间 + 当前任务执行时间;
- 有依赖关系:从Map中取出对应的依赖关系, 判断其依赖任务是否已经完成:
①如果已完成,那么可以前面任务执行的时间 + 当前任务执行时间;
②如果依赖任务没有完成,那么跳过该任务,判断下一个任务。
注意:在遍历任务数组的时候,可以对下标采用取余的方法来访问数组,这样可以达到循环访问任务数组,然后额外维护一个变量,用以记录任务执行的个数,当任务执行的个数达到任务数组的长度,即表示任务全都完成,结束循环。
示例代码
// 输入的处理均省略
// map用来记录依赖关系,依赖关系可能是多重依赖
Map<Integer, ArrayList<Integer>> map = new HashMap<>();
int compete = 0; //记录任务执行了多少个
int tmp = 0; //记录上一个任务执行完的时间
int[] res = new int[num]; //结果数组
while (compete != num) {
for (int i = 0; i < num; i++) {
if (compete == num) {
break;
}
if (res[i] == 0) {//如果当前任务没有执行
if (!map.containsKey(i)) {//先判断是否存在依赖关系,
// 如果没有依赖关系就用任务本身执行的时间+前面任务执行的时间
res[i] = arr[i] + tmp;
tmp = res[i]; // 记录已经执行完毕的任务的时间
compete++;//执行次数加1,作为判断退出循环的条件
} else {
//如果存在依赖关系,先取出map中的值,也就是包含依赖关系的ArrayList
ArrayList<Integer> Depend = map.get(i);
//判断其前置任务是否完成
boolean flag = false;
for (Integer depend : Depend) {
//对于每一个依赖任务,判断它是否已经执行
if (res[depend] == 0) {//如果依赖任务没有执行
flag = true;//flag置1
break;
}
}
if (!flag) {
//如果依赖任务执行完毕,即当前任务时间+前面任务执行的时间
res[i] = arr[i] + tmp;
tmp = res[i]; //前面任务执行的时间
compete++; //任务完成+1
}
}
}
}
}
题目三
到香港旅游,最后一站决定去迪士尼乐园打卡,因为返程机票已经订好,所以我们必须在剩余可游玩时间分钟内完成游玩,才能不耽误行程,请你为我们设计一条最佳游玩线路,选择规则如下:
- 游玩总时长不超过t,但最接近 t;
- 游玩时不想走回头路,仅向右或向下两个方向,畅玩到出口。
乐园被划分为一个 row * col 的方格区域地图,在每个方格区域上,标注了游玩的最佳时长,从入园口 [0,0] 出发,选定最佳游玩线路,一路畅玩到出口 [row-1, col-1]。
输入描述
首行输入以单个空格分割的三个正整数 row 和 col 以及 t,row代表地图行数(0 < row ≤13), col代表地图列数(0 < col ≤ 13),t代表剩余可游玩时间(0 < t ≤ 600) ;
输出描述
最佳游玩线路游玩总时长,若不存在,请输出-1。
示例输入
5 5 30
3 5 4 2 3
4 5 3 4 3
4 3 5 3 2
2 5 3 3 5
5 3 4 4 1
示例输出
30
思路
使用深度优先搜索向右、向下遍历即可,因为不会走回头路,所以不需要回溯。
使用两个方向数组,向右走:{0,1},向下走{1,0}。
需要注意判断边界条件,以及中途如果所耗时间已超可游玩时间,终止递归。
示例代码
public static int ans = -1;//定义一个全局变量,用来保存有效游玩路径的最大时间
public static void playPath(){
// 输入格式的处理省略, 使用案例输入作为初始数据
int time = 30, row = 5, col = 5;
int x = 0, y = 0; //起点的xy坐标
//这个数组保存各个游戏的游玩时间
int[][] play = new int[row][col]{
{3,5,4,2,3},
{4,5,3,4,3},
{4,3,5,3,2},
{2,5,3,3,5},
{5,3,4,4,1}
};
dfs(play, 0, 0, row, col, 0, time);
return ans;
}
public static void dfs(int[][] play, int x, int y, int row, int col,int t, int time){
//定义移动数组,如果已经游玩过的则为true
int[] right = new int[]{0, 1}; //向右移动
int[] down = new int[]{1, 0}; //向下移动
t += play[x][y];
if(x == row - 1 && y == col - 1 && t <= time){
ans = Math.max(ans, t);
}else{
int xr = x + right[0];
int yr = y + right[1];
if(xr < row && yr < col){
dfs(play, xr, yr, row, col, t, time);
}
int xd = x + down[0];
int yd = y + down[1];
if(xd < row && yd < col){
dfs(play, xd, yd, row, col, t, time);
}
}
}