2021-03-07 第 231 场周赛

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. 从第一个节点出发到最后一个节点的受限路径数

在这里插入图片描述

思路:(参考题解才懂得)这道题的做法分为三个步骤。

  1. 把原始所给的数组转换为邻接矩阵和邻接表,这里使用map的形式用来存储
  2. 计算出所有节点到n节点的最短路径
  3. 通过动态规划的方式找出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];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值