【leetcode刷题第37天】688.骑士在棋盘上的概率、10.02.变位词组、1300.转变数组后最接近目标值的数组和

这篇博客探讨了两个计算机科学问题:在国际象棋棋盘上,骑士进行随机移动后留在棋盘内的概率计算,以及如何对字符串数组进行排序,将所有变位词组合在一起。首先,通过动态规划方法解决了骑士概率问题,接着介绍了一个使用哈希映射和自定义类来组织和识别变位词的算法。最后,提出了一种使用二分查找解决寻找使数组和最接近目标值的整数值的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第三十七天

688 骑士在棋盘上的概率

在一个 n x n 的国际象棋棋盘上,一个骑士从单元格 (row, column) 开始,并尝试进行 k 次移动。行和列是 从 0 开始 的,所以左上单元格是(0,0) ,右下单元格是 (n - 1, n - 1)

象棋骑士有8种可能的走法,如下图所示。每次移动在基本方向上是两个单元格,然后在正交方向上是一个单元格。

每次骑士要移动时,它都会随机从8种可能的移动中选择一种(即使棋子会离开棋盘),然后移动到那里。

骑士继续移动,直到它走了 k 步或离开了棋盘。

返回 骑士在棋盘停止移动后仍留在棋盘上的概率

方法

容易知道,如果我们知道了在棋盘的位置(i,j)它的概率,那么对于它下一个能够跳到的位置(i + dx, j + dy),它的概率应该是所有能够跳到该位置的概率之和,而从i,j跳到i + dx, j + dy,它的概率是前者的概率乘上1/8。基于上述事实,我们可以使用动态规划来解决这个问题。

首先我们定义dp[step][i][j]表示用step步,从i,j出发,最终能够留在棋盘内的概率,则状态转移方程为:
dp[step][i][j]=18Σdp[step−1][i+dx][j+dy] dp[step][i][j]=\frac 1 8 \Sigma dp[step - 1][i+dx][j+dy] dp[step][i][j]=81Σdp[step1][i+dx][j+dy]
初始状态,当step0,概率为1

class Solution {
    public static int[][] dirs = {
        {2, 1}, {2, -1}, {-2, 1}, {-2, -1}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}
    };

    public double knightProbability(int n, int k, int row, int column) {
        double[][][] dp = new double[k + 1][n + 1][n + 1];
        for (int step = 0; step <= k; ++step){
            for (int i = 0; i < n; ++i){
                for (int j = 0; j < n; ++j){
                    if (step == 0) dp[step][i][j] = 1;
                    else{
                        for (int delta = 0; delta < 8; ++delta){
                            if (i + dirs[delta][0] >= 0 && i + dirs[delta][0] < n && j + dirs[delta][1] >= 0 && j + dirs[delta][1] < n){
                                dp[step][i][j] += 0.125 * dp[step - 1][i + dirs[delta][0]][j + dirs[delta][1]]; 
                            }
                        }
                    }
                }
            }
        }
        return dp[k][row][column];
    }
}

10.02 变位词组

编写一种方法,对字符串数组进行排序,将所有变位词组合在一起。变位词是指字母相同,但排列不同的字符串。

**注意:**本题相对原题稍作修改

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"],
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

说明:

  • 所有输入均为小写字母。

  • 不考虑答案输出的顺序。

方法

使用一个长度为26的数组记录每一个单词中字母出现的数量,用于作为是否属于同一类单词的唯一标识。

class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        Map<WordRegardLessOrder, List<String>> map = new HashMap<>();
        for (String str : strs) {
            WordRegardLessOrder word = new WordRegardLessOrder(str);
            if (map.containsKey(word)) map.get(word).add(str);
            else{
                ArrayList<String> words = new ArrayList<>();
                words.add(str);
                map.put(word, words);
            }
        }
        return new ArrayList<>(map.values());
    }
}

class WordRegardLessOrder{
    String word;
    public WordRegardLessOrder(String s){word = s;}
    @Override public boolean equals(Object p){
        WordRegardLessOrder cmp = (WordRegardLessOrder) p;
        if (word.length() != cmp.word.length()) return false;
        int[] cnt = new int[26];
        for (int i = 0; i < word.length(); ++i){
            cnt[word.charAt(i) - 'a']++;
            cnt[cmp.word.charAt(i) - 'a']--;
        }
        for (int i : cnt) if (i != 0) return false;
        return true;
    }
    @Override public int hashCode(){
        int res = 0;
        for (int i = 0; i < word.length(); ++i) res ^= word.charAt(i);
        return res;
    }
}

1300 转变数组后最接近目标值的数组和

给你一个整数数组 arr 和一个目标值 target ,请你返回一个整数 value ,使得将数组中所有大于 value 的值变成 value 后,数组的和最接近 target (最接近表示两者之差的绝对值最小)。

如果有多种使得和最接近 target 的方案,请你返回这些整数中的最小值。

请注意,答案不一定是 arr 中的数字。

示例 1:

输入:arr = [4,9,3], target = 10
输出:3
解释:当选择 value 为 3 时,数组会变成 [3, 3, 3],和为 9 ,这是最接近 target 的方案。

提示:

  • 1 <= arr.length <= 10^4

  • 1 <= arr[i], target <= 10^5

方法

容易知道答案的左边界是0,右边界是数组的最大值,我们可以使用二分查找来解决这个问题。

如果我们当前正在搜索的值mid所组成的新数组的和比target大,我们调整r=mid-1否则l=mid+1

返回答案时我们只需要比较lr哪个的差值更小即可。

class Solution {
    public int findBestValue(int[] arr, int target) {
        int l = 0, r = 0;
        for (int i : arr) r = Math.max(r, i);
        while (l <= r){
            System.out.println(l + " " + r);
            int mid = (l + r) >> 1;
            if (getDiffer(arr, mid) > target){
                r = mid - 1;
            }
            else if (getDiffer(arr, mid) == target){
                return mid;
            }
            else{
                l = mid + 1;
            }
        }
        //System.out.println(l + " " + r);
        return Math.abs(getDiffer(arr, r) - target) > Math.abs(getDiffer(arr, l) - target) ? l : r;
    }

    public int getDiffer(int[] arr, int value) {
        int res = 0;
        for (int i : arr) {
            if (i < value) res += i;
            else res += value;
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值