2021-03-07 第 231 场周赛
1784. 检查二进制字符串字段
思路:模拟题,只需要判断1是否连续出现即可
class Solution {
public boolean checkOnesSegment(String _s) {
char[] s = _s.toCharArray();
int last = -1,cnt = 0;
for(int i = 0;i < s.length;i++) {
if(s[i] == '1') {
if(i - last > 1) {
return false;
}
last = i;
}
}
return true;
}
}
更加巧妙的做法是,只需要判断“01”这个字符串是否出现过即可。因为字符串是以1开头的,如果1是连续的,那么“0”后面不能出现“1”。
class Solution {
public boolean checkOnesSegment(String s) {
return !s.contains("01");
}
}
1785. 构成特定和需要添加的最少元素
思路:只需要计算当前数组的和,算出到目标值的差,不断的用limit值往里填,数组和小于目标值用正limit数填,大于目标值用负limit数填,计算填充的次数即可。
class Solution {
public int minElements(int[] nums, int limit, int goal) {
long sum = 0;
for(int num : nums) sum += num;
long req = Math.abs(sum-goal);
return (int) Math.ceil(1.0*req/limit);
}
}
1786. 从第一个节点出发到最后一个节点的受限路径数
思路:(参考题解才懂得)这道题的做法分为三个步骤。
- 把原始所给的数组转换为邻接矩阵和邻接表,这里使用map的形式用来存储
- 计算出所有节点到n节点的最短路径
- 通过动态规划的方式找出1到n的受限路径数
import java.util.*;
class Solution {
Map<Integer, Map<Integer,Integer>> map = new HashMap<>();
int mod = (int) (1e9 + 7);
public int[] dijkstra(int n) {
int[] dis = new int[n+1];
boolean[] vis = new boolean[n+1];
Arrays.fill(dis, Integer.MAX_VALUE);
dis[n] = 0;
// 基于最小堆的dijkstra算法,每次找到距离最短的点
PriorityQueue<int[]> q = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1]-o2[1];
}
});
q.offer(new int[]{n,0});
while(!q.isEmpty()) {
int[] cur = q.poll();
int node = cur[0], d = cur[1];
if(vis[node]) continue;
vis[node] = true;
Map<Integer, Integer> nodeMap = map.getOrDefault(node, null);
if(nodeMap != null) {
// 松弛其他节点到节点n的距离
for(int j : nodeMap.keySet()) {
if(!vis[j]) {
dis[j] = Math.min(dis[j], dis[node] + nodeMap.get(j));
q.offer(new int[]{j,dis[j]});
}
}
}
}
return dis;
}
public int countRestrictedPaths(int n, int[][] edges) {
for(int[] e : edges) {
// 预处理所有的边权。 i j w -> i : { j : w } + j : { i : w }
int i = e[0], j = e[1], w = e[2];
// 获取i对应的邻接表
Map<Integer,Integer> iMap = map.getOrDefault(i, new HashMap<>());
iMap.put(j,w);
map.put(i,iMap);
// 获取j对应的邻接表
Map<Integer,Integer> jMap = map.getOrDefault(j, new HashMap<>());
jMap.put(i,w);
map.put(j,jMap);
}
// 计算所有路径到n的最短路径,反过来就是计算n到其他节点的最短路径,可以使用dijkstra算法
int[] dis = dijkstra(n);
// dp[i]表示从点i到点n的受限路径数量,那么在计算dp[i]时,需要找出所有与i相邻的节点
// 只有dis[j] < dis[i]的节点的受限路径数量才能加入到dp[i]中,即受限路径为i,j,...
long[] dp = new long[n+1]; dp[n] = 1;
List<Integer> list = new ArrayList<>();
for (int i = 1; i < n; i++) list.add(i);
Collections.sort(list, (o1, o2) -> dis[o1]-dis[o2]);
for(int i : list) {
Map<Integer,Integer> iMap = map.getOrDefault(i,null);
if(iMap != null) {
for(int j : iMap.keySet()) {
if(dis[i] > dis[j]) {
dp[i] += dp[j];
dp[i] %= mod;
}
}
}
if(i == 1) break;
}
return (int) dp[1];
}
}
1787. 使所有区间的异或结果为零
思路:参考自评论区的题解
import java.util.*;
class Solution{
public int minChanges(int[] nums, int k) {
int n = nums.length;
int[] cnt = new int[k];
Map<Integer,Integer>[] freq = new Map[k];
for(int i = 0;i < k;i++) freq[i] = new HashMap<>();
for (int i = 0; i < n; i++) {
// 统计每一组元素的个数
cnt[i%k]++;
// 每一列组里每个元素出现的次数
freq[i%k].put(nums[i], freq[i%k].getOrDefault(nums[i],0)+1);
}
// dp[i][j] 表示前 i 列异或结果为 j 时的最小操作次数
// 转移方成为:dp[i][j] = dp[i-1][v] + cost,cost为第i列的修改个数
// 这个式子表示,对于任意的 dp[i-1][v] ,总能修改第 i 列所有元素为某一个数字,使前 i 列异或结果为 j
// 对于第 i 列元素可能的取值,一种情况是修改为第 i 列中已存在的数,一种情况是除此之外的数,
// 我们实际上只用算已存在的数,除此之外的其实都是修改第 i 列的所有元素,这些只用算一次,不用都算一遍。
// 那么可以预处理算出当第 i 列都要修改时,异或结果为某个值 j 时的最小操作次数 dp[i][j] 为多少。
// 怎么算呢?注意到 dp[i-1][v] ,我们可以算出在前 i-1 列中,对于任意的取值 v ,找到一个最小的操作次数 dp[i-1][v] ,
// 那么这个值加上第i列的元素个数,就是前 i 列的最小操作次数,这也是前 i 列所有最小操作次数的上限。
// 算出这个,就可以在遍历 0 ~ 1023 中不是已存在的数时,直接使用此值即可;
// 是已存在的,那就还得进一步用转移方程算出结果。
int[][] dp = new int[k+1][1024 + 10];
for (int[] d : dp) Arrays.fill(d, -1);
dp[0][0] = 0;
for(int i = 1;i <= k;i++) {
int minOperator = -1;
for (int j = 0; j < 1024; ++j) {
if (dp[i-1][j] == -1) continue;
if (minOperator == -1 || minOperator > dp[i-1][j]) minOperator = dp[i-1][j];
}
minOperator += cnt[i-1]; //全部修改的个数
for(int j = 0;j < 1024;j++) {
if(dp[i][j] == -1) dp[i][j] = minOperator;
if(dp[i-1][j] == -1) continue;
for(Map.Entry<Integer,Integer> entry : freq[i-1].entrySet()) {
int key = entry.getKey(); //值
int value = entry.getValue(); //次数
int cost = cnt[i-1] - value;
if(dp[i][j^key] == -1 || dp[i][j^key] > dp[i-1][j] + cost) {
dp[i][j^key] = dp[i-1][j] + cost;
}
}
}
}
return dp[k][0];
}
}