1. 小米笔试题——升级蓄水池
题目描述:
在米兔生活的二维世界中,建造蓄水池非常简单。
一个蓄水池可以用n个坐标轴上的非负整数表示,代表区间为【0-n】范围内宽度为1的墙壁的高度。
如下图1,黑色部分是墙壁,墙壁的高度是[0,1,0,2,1,0,1,3,2,1,2,1] ,蓝色部分是蓄水的面积,可以看出蓄水池最大蓄水容量是6。
现在米兔想通过增加某些墙壁的高度对蓄水池扩容,但是经费有限,最多只能增加最多m的高度,增加高度只能在【0-n】范围内,高度为0的区域也是可以增加的,为了追求最大的性价比,米兔想要找到一种最优方案,使扩容后蓄水池的容量最大,你能帮帮他么?
提示:
对于样例,图2,图3,是样例可能的两种扩容方案,显然图2是比图3更优的方案
关于题目数据范围
20%的数据, n<10,m<10,蓄水池最大高度小于10
50%的数据, n<100,m<100,蓄水池最大高度小于100
100%的数据, n<1000,m<1000,蓄水池最大高度小于1000
输入
第一行为一个数字n
接下来n行,每行一个数字,代表n个墙壁的高度
最后一行为一个数字m
输出
一个数字,表示扩容之后蓄水池能达到的最大容量
样例输入
12
0
1
0
2
1
0
1
3
2
1
2
1
2
样例输出
12
分析
LeetCode上 trap water 题目的改进版, 当时没有解出这题,现在回想起来可以用dfs来遍历所有的可能。
import java.util.*;
public class LeetCode {
static int maxCapicity = 0;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] array = new int[n];
for (int i = 0; i < n; i++) {
array[i] = sc.nextInt();
}
int m = sc.nextInt();
maxCapicity=capacity(array);
dfs(0,array,m);
System.out.println(maxCapicity);
}
static void dfs(int index, int[] array, int m) {
if(index>=array.length) return;
if(m==0){
maxCapicity=Math.max(maxCapicity, capacity(array));
}
for (int i = 0; i <= m; i++) {
array[index] += i;
dfs(index + 1, array, m - i);
array[index] -= i;
}
}
static int capacity(int[] array) {
int ans = 0;
int n = array.length;
int[] left_max = new int[n];
int[] right_max = new int[n];
left_max[0] = array[0];
right_max[n - 1] = array[n - 1];
for (int i = 1; i < array.length; i++) {
left_max[i] = Math.max(left_max[i - 1], array[i]);
}
for (int i = array.length - 2; i >= 0; i--) {
right_max[i] = Math.max(right_max[i + 1], array[i]);
}
for (int i = 0; i < n; i++) {
ans += Math.min(right_max[i], left_max[i]) - array[i];
}
return ans;
}
}
能解出示例的输入,但是没有平台提供的测试用例所以不知道是否能通过所有的测试用例,仅供参考。
2. 哔哩哔哩笔试题——字符串数字表达式计算值
输入几行字符串形式的表达式,只包含非负整数,加号,减号和乘号,输入其计算值。输入以字符串END结束
输入样例:
3+4*5*6+4-7*4+4-2
3+4*2-1
END
输出
101
10
分析
将原字符串按照加减号分割成字符串数组,计算每个的实际值后再压入栈中。之后倒序扫描原数组,遇到加号或者减号就从栈中弹出两个数字,计算值后再压入栈,遇到乘号则无视。最后栈中剩下的数字就是计算结果。
实际测试法发现分割后的字符串计算后的值应该倒序进栈,然后顺序扫描原数组才对,否则在计算减号时会有错误。
import java.util.*;
public class LeetCode {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
List<String> expressions = new ArrayList<>();
List<Integer> ans = new ArrayList<>();
while (!str.equals("END")) {
expressions.add(str);
str = sc.nextLine();
}
for (String s : expressions) ans.add(calculate(s));
for (int a : ans) {
System.out.println(a);
}
}
private static int calculate(String str) {
Stack<Integer> digit = new Stack<>();
String[] strs = str.split("\\+|\\-");
for (int i = strs.length - 1; i >= 0; i--) {
String s = strs[i];
if (s.contains("*")) {
String[] multi = s.split("\\*");
int result = 1;
for (String mu : multi) result *= Integer.parseInt(mu);
digit.push(result);
} else {
digit.push(Integer.parseInt(s));
}
}
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == '+' || str.charAt(i) == '-') {
int num1 = digit.pop();
int num2 = digit.pop();
int result = (str.charAt(i) == '+' ? num1 + num2 : num1 - num2);
digit.push(result);
}
}
return digit.peek();
}
}
3. 求旅游完所有景点需要的最少天数
去一个地方出差,顺便取旅游,根据导游提供的方案,每一天可以去当地的一个景点。选择从某一天开始自己的假期,使得在最短的天数内能去所有导游提供的景点。
输入示例1:(开始一个数字表示总共有多少天。然后第一列表示第几天,第二列表示第几天可以去的景点编号)
7
1,7
2,5
3,1
4,7
5,2
6,5
7,1
输出:
4
即从第2天开始假期,持续到第5天,总共4天。或者从第5天开始假期,也是4天。
输入示例2:
11
1,2
2,3
3,1
4,3
5,3
6,7
7,2
8,2
9,1
10,1
11,3
输出:
5,即从第3天到第7天。
分析
可以通过循环遍历的方式来穷举所有的可能,不过会超时,这题关键时要求用有效率的方法来解。想到的法还是根据循环遍历而来,只不过原来的循环会有重复计算的可能,我们要去掉这个重复计算的部分。当从以位置 i 为起始点向后遍历的时候,记住不同景点的出现次数。如果到了某一点 j 使得从 i 到 j 能够取到所有景点时,停止向后。转而从i的下一位开始,更新当前能到达的景点的计数器,如果此时还能到达所以景点则继续往下一位,如果不能则往 j 之后继续遍历直至能到达所有景点,重复这个过程即可。(我感觉有点像Leetcode中Minimum Window Substring这一题的解法,额,看了一下,感觉根本是一个题目 )
import java.util.*;
public class LeetCode {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String[] trip = new String[n];
sc.nextLine();
for (int i = 0; i < n; i++) {
String[] strs = sc.nextLine().split(",");
trip[i] = strs[1]; // 数组索引直接0~n-1对应第1天到第n天
}
// 先遍历一遍确定有几个不同的景点
Set<String> spots = new HashSet<>();
for (String s : trip) {
spots.add(s);
}
Map<String, Integer> map = new HashMap<>(); // 景点计数器,若要能到达所有景点则至少每个景点要到达一次
for (String s : spots) {
map.put(s, 0);
}
int begin = 0, end = 0, d = Integer.MAX_VALUE;
while (end < n) { // 先从begin=0开始,找左边第一个包含所有景点的时间段
String str = trip[end];
if (map.containsKey(str)) map.put(str, map.get(str) + 1);
//检查当前是否能够到达所有景点
boolean canTravelAll = true;
for (String s : spots) {
if (map.get(s) <= 0) canTravelAll = false;
}
//如果可以则回到左边,往左移缩小范围,如果不能则继续在右边向后移动
while (canTravelAll) {
d = Math.min(d, end - begin); // 更新最小范围
String s = trip[begin];
map.put(s, map.get(s) - 1); // 左移后要判断当前是否包括了所有的景点,是的话继续左移
if (map.get(s) <= 0) canTravelAll = false;
}
end++;
}
System.out.println(d);
}
}