题目
做项目的最大收益问题
- 链接 做项目的最大收益问题
- 题目描述
给定两个整数W和K,W代表你拥有的初始资金,K代表你最多可以做K个项目。再给定两个长度为N的正数数组costs[]和profits[],代表一共有N个项目,costs[i]和profits[i]分别表示第i号项目的启动资金与做完后的利润(注意是利润,如果一个项目的启动资金为10,利润为4,代表该项目最终的收入为14)。你不能并行只能串行地做项目,并且手里拥有的资金大于或等于某个项目的启动资金时,你才能做这个项目。该如何选择做项目,能让你最终的收益最大?返回最后能获得的最大资金。
[要求] 时间复杂度为 O(klogn),空间复杂度为 O(n)。
和原题不一样的是,这里返回值是long
类型。
- 输入与输出
第一行三个整数N, W, K。表示总的项目数量,初始资金,最多可以做的项目数量 第二行有N个正整数,表示costs数组
第三行有N个正整数,表示profits数组。
输入:
4 3 2
5 4 1 2
3 5 3 2
输出:
11
解释:
初始资金为3,最多做两个项目,每个项目的启动资金与利润见costs和profits。最优选择为:先做2号项目,做完之后资金增长到6。然后做1号项目,做完之后资金增长到11。其他的任何选择都不会比这种选择好,所以返回11。
最长无重复数组
- 链接 最长无重复数组
- 题目描述
给定一个数组arr,返回arr的最长无重复元素子数组的长度,无重复指的是所有数字都不相同。
子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组
- 输入与输出
输入:
[2,3,4,5]
输出:
4
解释:
[2,3,4,5]是最长子数组
思路
做项目的最大收益问题
- 建立节点,记录每个项目的 cost 和 profits;
- 生成比较器,小根堆保存每个节点,大根堆根据情况(K 的取值、当前资金)保存值;
- 判断当前资金是否满足条件;
- 返回结果。
最长无重复子串
暴力解法
- 双层循环遍历数组,如果有重复的,就打破循环,判断长度。
滑动窗口
- 定义 Map,key = arr[i] ,value = arr[i] 的下标;
- 遍历数组,如果当前出现过,那么更新左窗口的最大值;
- 每次循环更新长度;
- 返回结果。
代码
做项目的最大收益问题
import java.util.Comparator;
import java.util.PriorityQueue;
public class Slution{
/**
* return the max money
* @param costs int整型一维数组 the costs
* @param profits int整型一维数组 the profits
* @param W int整型 the money you have first
* @param K int整型 max projects
* @return long长整型
*/
public static long maxMoney (int[] costs, int[] profits, int W, int K) {
// write code here
// 返回结果是 long 如果不定义 long res 的话结果可能会错误
long res = W;
// 将项目的花费与收益存在 nodes 数组中
Node[] nodes = new Node[profits.length];
for (int i = 0; i < profits.length; i++) {
nodes[i] = new Node(profits[i], costs[i]);
}
PriorityQueue<Node> minQueue = new PriorityQueue<>(new MinHeapCom());
PriorityQueue<Node> maxQueue = new PriorityQueue<>(new MaxHeapCom());
// 建立小根堆, 按照 cost(花费) 排序
for (int i = 0; i < profits.length; i++) {
minQueue.add(nodes[i]);
}
// K 代表最多可做项目数量, 因此有了这个循环
for (int i = 0; i < K; i++) {
// 当 minQueue 不为空 并且 花费小于 W(初始资金) 时, 大根堆添加节点
while (!minQueue.isEmpty() && minQueue.peek().cost <= res) {
maxQueue.add(minQueue.poll());
}
// 如果资金不够做下一个项目, 那么返回收益 res
if (maxQueue.isEmpty()) {
return res;
}
// 如果够, 那么 res += 当前项目的收益
res += maxQueue.poll().profit;
}
return res;
}
// 小根堆
static class MinHeapCom implements Comparator<Node> {
@Override
public int compare(Node o1, Node o2) {
return o1.cost - o2.cost;
}
}
// 大根堆
static class MaxHeapCom implements Comparator<Node> {
@Override
public int compare(Node o1, Node o2) {
return o2.profit - o1.profit;
}
}
// 定义节点
static class Node {
int profit;
int cost;
public Node(int profit, int cost) {
this.profit = profit;
this.cost = cost;
}
}
}
最长无重复子串
暴力
public int maxLength (int[] arr) {
// write code here
// 定义结果
int res = -1;
for (int i = 0; i < arr.length; i++) {
// 每次使用 set 保存
Set<Integer> set = new HashSet<>();
// 添加当前元素
set.add(arr[i]);
for (int j = i+1; j < arr.length; j++) {
// 如果添加失败, 那么说明有重复的元素,打破循环
if (!set.add(arr[j])) {
break;
}
}
// 比较当前长度与原来记录的长度
res = Math.max(res,set.size());
}
return res;
}
滑动窗口
public static int maxLength (int[] arr) {
// 如果 arr.length < 2 直接返回结果
if (arr.length < 2) {
return arr.length;
}
// 定义结果
int res = -1;
// 使用 map 保存信息
// key 代表当前元素, value 代表当前元素的下标
Map<Integer, Integer> map = new HashMap<>();
// 遍历数组
for (int i = 0, begin = 0; i < arr.length; i++) {
// 如果 map 中含有当前元素, 说明重复了
if (map.containsKey(arr[i])) {
// 此时需要判断左边界与原来的左边界的值, 如果
begin = Math.max(map.get(arr[i]+1), begin);
}
res = Math.max(i - begin+1, res);
map.put(arr[i],i);
}
return res;
}