【數組】A058_有序矩陣中的第 k 個最小數組和(暴力 / bfs / 二分(代辦))

一、Problem

You are given an m * n matrix, mat, and an integer k, which has its rows sorted in non-decreasing order.

You are allowed to choose exactly 1 element from each row to form an array. Return the Kth smallest array sum among all possible arrays.

Input: mat = [[1,3,11],[2,4,6]], k = 5
Output: 7
Explanation: Choosing one element from each row, the first k smallest sum are:
[1,2], [1,4], [3,2], [3,4], [1,6]. Where the 5th sum is 7.  

Input: mat = [[1,10,10],[1,4,5],[2,3,6]], k = 7
Output: 9
Explanation: Choosing one element from each row, the first k smallest sum are:
[1,1,2], [1,1,3], [1,4,2], [1,4,3], [1,1,6], [1,5,2], [1,5,3]. Where the 7th sum is 9.  

提示:

  • m == mat.length
    n == mat.length[i]
    1 <= m, n <= 40
    1 <= k <= min(200, n ^ m)
    1 <= mat[i][j] <= 5000
    mat[i] 是一個非遞減數組

方法一:暴力

估計一下運算量:m × n 最大爲 1600,k 最小爲 200,我們每次只保留前 k 個元素,也就是 200 × 1600,加上一些排序,集合的複製,絕對不會超時,而且會很快…

講大致的思路吧:

  • 遍歷 mat 的每一行,將每一行和上一行沒有被篩出來的 k 個組合都組合一下。
  • 每次組合完,再排序,排完在選前 k 個。
  • 然後再遍歷 mat 的下一行。
public int kthSmallest(int[][] mat, int k) {
    List<Integer> kl = new ArrayList<>();
    for (int[] a : mat) {
        List<Integer> t = new ArrayList<>();
        for (int n1 : a) {
            if (kl.size() == 0) 
                t.add(n1);
            else for (int n2 : kl)
                t.add(n1+n2); 
        }
        Collections.sort(t);
        if (t.size() > k)
            t = t.subList(0, k);
        kl = new ArrayList<>(t);
    }
    return kl.get(kl.size()-1);
}

複雜度分析

  • 時間複雜度:O(m×n×k)O(m × n × k)
  • 空間複雜度:O(n)O(n)

參考方法二:bfs

別人的思路,不太懂 hhh…😂

class Solution {
    public int kthSmallest(int[][] mat, int k) {
        int m = mat.length;
        int n = mat[0].length;
        int initSum = 0;      
        for(int i=0; i<m; i++) {
            initSum += mat[i][0];          
        }
        if(k == 1) return initSum;
        
        //pre process,mat保存增值
        for(int i=0; i<m; i++) {
            for(int j=n-1; j>0; j--) {
                mat[i][j] -= mat[i][j-1];
            }
        }
        Set<String> visited = new HashSet<>();
        int[] list = new int[m]; //初始都是0
        Node init = new Node(initSum, list);
        PriorityQueue<Node> queue = new PriorityQueue<>((n1,n2)->n1.sum-n2.sum);
        queue.add(init);
        visited.add(init.code);
        int cnt = 0;
        while(!queue.isEmpty()) {
            Node node = queue.poll();           
            cnt++;
            if(cnt == k) {
                return node.sum;
            } 
            int[] pre = node.ins;
            //遍歷所有的下一個位置
            for(int i=0; i<m; i++) {
                //當前行已經到最後一列了,不能繼續前進
                if(pre[i]== n-1) continue;
                int[] tmp = Arrays.copyOf(pre, m);
                tmp[i]++;
                //計算下一次的和,只需要添加增值
                int cur = node.sum + mat[i][tmp[i]];
                Node next = new Node(cur, tmp);
                //是否已經計算過
                if(visited.contains(next.code)) {
                    continue;
                }
                visited.add(next.code);
                queue.add(new Node(cur, tmp));
            }     
        }
        return 0;
    }
    
    class Node {
        int sum;
        int[] ins; //indexList
        String code; //去重code

        public Node(){}
        public Node(int sum, int[] ins){
            this.sum = sum;
            this.ins = ins;
            StringBuilder sb = new StringBuilder();
            for(int index : ins) {
                sb.append(index).append(",");
            }
            this.code = sb.toString();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章