[Leetcode Weekly Contest]263
链接:LeetCode
[Leetcode]2042. 检查句子中的数字是否递增
句子是由若干 token 组成的一个列表,token 间用 单个 空格分隔,句子没有前导或尾随空格。每个 token 要么是一个由数字 0-9 组成的不含前导零的 正整数 ,要么是一个由小写英文字母组成的 单词 。
示例,"a puppy has 2 eyes 4 legs" 是一个由 7 个 token 组成的句子:"2" 和 "4" 是数字,其他像 "puppy" 这样的 tokens 属于单词。
给你一个表示句子的字符串 s ,你需要检查 s 中的 全部 数字是否从左到右严格递增(即,除了最后一个数字,s 中的 每个 数字都严格小于它 右侧 的数字)。
如果满足题目要求,返回 true ,否则,返回 false 。
遍历即可。
class Solution {
public boolean areNumbersAscending(String s) {
int last = -1;
for(var ss:s.split(" ")) {
if(isDigit(ss)) {
int cur = Integer.parseInt(ss);
if(cur <= last) return false;
last = cur;
}
}
return true;
}
public boolean isDigit(String s) {
for(var ch:s.toCharArray()) {
if (!Character.isDigit(ch)) return false;
}
return true;
}
}
[Leetcode]2043. 简易银行系统
你的任务是为一个很受欢迎的银行设计一款程序,以自动化执行所有传入的交易(转账,存款和取款)。银行共有 n 个账户,编号从 1 到 n 。每个账号的初始余额存储在一个下标从 0 开始的整数数组 balance 中,其中第 (i + 1) 个账户的初始余额是 balance[i] 。
请你执行所有 有效的 交易。如果满足下面全部条件,则交易 有效 :
指定的账户数量在 1 和 n 之间,且
取款或者转账需要的钱的总数 小于或者等于 账户余额。
实现 Bank 类:
Bank(long[] balance) 使用下标从 0 开始的整数数组 balance 初始化该对象。
boolean transfer(int account1, int account2, long money) 从编号为 account1 的账户向编号为 account2 的账户转帐 money 美元。如果交易成功,返回 true ,否则,返回 false 。
boolean deposit(int account, long money) 向编号为 account 的账户存款 money 美元。如果交易成功,返回 true ;否则,返回 false 。
boolean withdraw(int account, long money) 从编号为 account 的账户取款 money 美元。如果交易成功,返回 true ;否则,返回 false 。
按照题意实现即可,注意判断账户是否有效。
class Bank {
private long[] balance;
private final int N;
public Bank(long[] balance) {
this.balance = balance;
this.N = balance.length;
}
public boolean isAvailableAccount(int account) {
return account >=1 && account <= this.N;
}
public boolean hasEnoughMoney(int account, long money) {
return this.balance[account-1] >= money;
}
public boolean transfer(int account1, int account2, long money) {
if(!isAvailableAccount(account1) || !isAvailableAccount(account2)) return false;
if(!hasEnoughMoney(account1,money)) return false;
this.balance[account1-1] -= money;
this.balance[account2-1] += money;
return true;
}
public boolean deposit(int account, long money) {
if(!isAvailableAccount(account)) return false;
this.balance[account-1] += money;
return true;
}
public boolean withdraw(int account, long money) {
if(!isAvailableAccount(account) || !hasEnoughMoney(account,money)) return false;
this.balance[account-1] -= money;
return true;
}
}
/**
* Your Bank object will be instantiated and called as such:
* Bank obj = new Bank(balance);
* boolean param_1 = obj.transfer(account1,account2,money);
* boolean param_2 = obj.deposit(account,money);
* boolean param_3 = obj.withdraw(account,money);
*/
[Leetcode]2044. 统计按位或能得到最大值的子集数目
给你一个整数数组 nums ,请你找出 nums 子集 按位或 可能得到的 最大值 ,并返回按位或能得到最大值的 不同非空子集的数目 。
如果数组 a 可以由数组 b 删除一些元素(或不删除)得到,则认为数组 a 是数组 b 的一个 子集 。如果选中的元素下标位置不一样,则认为两个子集 不同 。
对数组 a 执行 按位或 ,结果等于 a[0] OR a[1] OR ... OR a[a.length - 1](下标从 0 开始)。
这道题中要找的是 子序列 中,包含几个可行解,很容易的可以想到 dfs:
观察数据规模, 1 ≤ n ≤ 16,最暴力的 dfs 也才 2^16 ,可以接受还需要找到 按位或 的 最大值 。观察到所有的数字都是正数,则显然有 \(a\ |\ b ≥ max(a, b)\),即一个正数A按位或另一个正数B,得到的结果C一定是不减的。
由此我们可以知道:我们要寻找的最大值 = 所有数字按位或结果。
另外,可用在回溯过程中剪枝,假设我们找到了一个组合,其位或结果为最大值,则接下来剩余的m个数都不需要再搜索了,因为我们想到这m个数加或者不加,其结果不变,则直接返回2**m即可。
class Solution {
private int mx = 0;
private int count = 0;
public int countMaxOrSubsets(int[] nums) {
for(var num:nums) {
mx = mx | num;
}
dfs(nums,-1,0);
return count;
}
public void dfs(int[] nums,int startIndex,int cur) {
int n = nums.length;
if(cur == mx) {
// count += Math.pow(2, n-startIndex-1);
count += 1<<(n-startIndex-1);
return;
}
for(int i=startIndex+1;i<nums.length;++i) {
dfs(nums,i,cur|nums[i]);
}
}
}
[Leetcode]2045. 到达目的地的第二短时间
城市用一个 双向连通 图表示,图中有 n 个节点,从 1 到 n 编号(包含 1 和 n)。图中的边用一个二维整数数组 edges 表示,其中每个 edges[i] = [ui, vi] 表示一条节点 ui 和节点 vi 之间的双向连通边。每组节点对由 最多一条 边连通,顶点不存在连接到自身的边。穿过任意一条边的时间是 time 分钟。
每个节点都有一个交通信号灯,每 change 分钟改变一次,从绿色变成红色,再由红色变成绿色,循环往复。所有信号灯都 同时 改变。你可以在 任何时候 进入某个节点,但是 只能 在节点 信号灯是绿色时 才能离开。如果信号灯是 绿色 ,你 不能 在节点等待,必须离开。
第二小的值 是 严格大于 最小值的所有值中最小的值。
例如,[2, 3, 4] 中第二小的值是 3 ,而 [2, 2, 4] 中第二小的值是 4 。
给你 n、edges、time 和 change ,返回从节点 1 到节点 n 需要的 第二短时间 。
注意:
你可以 任意次 穿过任意顶点,包括 1 和 n 。
你可以假设在 启程时 ,所有信号灯刚刚变成 绿色 。
BFS。由于 \(\textit{time}\) 和 \(\textit{change}\) 是固定的,经过多少条边就知道花费了多少时间。因此这题本质上可以看成边权均为 1 的图,我们要求的就是 1 到 n 的严格次短路的长度,知道长度就知道花费的时间。
class Solution {
public int secondMinimum(int n, int[][] edges, int time, int change) {
List<Integer>[] adj = new ArrayList[n];
for(int i = 0; i < n; ++i) adj[i] = new ArrayList<>();
int[] dis = new int[n];
boolean[] sec = new boolean[n];
for(int[] edge : edges) {
int v = edge[0] - 1, u = edge[1] - 1;
adj[v].add(u);adj[u].add(v);
}
for(int i = 1; i < n; ++i) dis[i] = Integer.MAX_VALUE;
Queue<Integer> q = new LinkedList<>();
q.offer(0);
while(!q.isEmpty()) {
int head = q.poll();
for(int u : adj[head]) {
if(dis[u] > dis[head] + 1) {
dis[u] = dis[head] + 1;
q.offer(u);
}
else if(dis[u] == dis[head] || (sec[head] && dis[u] == dis[head] + 1)){
if(!sec[u]) q.offer(u);
sec[u] = true;
}
}
}
int path = dis[n - 1] + (sec[n - 1] ? 1 : 2);
return lenToTime(path, time, change);
}
private int lenToTime(int len, int time, int change) {
int cur = 0, tol = 0;
for(int i = 0; i < len; i++) {
tol += time;
if(i == len - 1) break;
int period = tol / change;
if((period & 1) == 1) {
tol += (period + 1) * change - tol;
}
}
return tol;
}
}