标签: 贪心

LeetCode刷题【807】保持城市天际线

给你一座由 n x n 个街区组成的城市,每个街区都包含一座立方体建筑。给你一个下标从 0 开始的 n x n 整数矩阵 grid ,其中 grid[r][c] 表示坐落于 rc 列的建筑物的 高度

城市的 天际线 是从远处观察城市时,所有建筑物形成的外部轮廓。从东、南、西、北四个主要方向观测到的 天际线 可能不同。

我们被允许为 任意数量的建筑物 的高度增加 任意增量(不同建筑物的增量可能不同) 。 高度为 0 的建筑物的高度也可以增加。然而,增加的建筑物高度 不能影响 从任何主要方向观察城市得到的 天际线

不改变 从任何主要方向观测到的城市 天际线 的前提下,返回建筑物可以增加的 最大高度增量总和

 

示例 1:

输入:grid = [[3,0,8,4],[2,4,5,7],[9,2,6,3],[0,3,1,0]]
输出:35
解释:建筑物的高度如上图中心所示。
用红色绘制从不同方向观看得到的天际线。
在不影响天际线的情况下,增加建筑物的高度:
gridNew = [ [8, 4, 8, 7],
            [7, 4, 7, 7],
            [9, 4, 8, 7],
            [3, 3, 3, 3] ]

示例 2:

输入:grid = [[0,0,0],[0,0,0],[0,0,0]]
输出:0
解释:增加任何建筑物的高度都会导致天际线的变化。

 

提示:

  • n == grid.length
  • n == grid[r].length
  • 2 <= n <= 50
  • 0 <= grid[r][c] <= 100
Related Topics
  • 贪心
  • 数组
  • 矩阵

  • 👍 229
  • 👎 0
  • 题面的示例已经给出了基本实现思路
    题面的示例已经给出了基本实现思路,顺着这个思路写代码就对了、

    代码

    class Solution {
        public int maxIncreaseKeepingSkyline(int[][] grid) {
            int[] rowMin = new int[grid.length];
            int[] colMin = new int[grid[0].length];
            for (int row = 0; row < grid.length; row++) {
                for (int col = 0; col < grid[row].length; col++) {
                    int height = grid[row][col];
                    rowMin[row] = Math.max(rowMin[row],height);
                    colMin[col] = Math.max(colMin[col],height);
                }
            }
            int ans = 0;
            for (int row = 0; row < grid.length; row++) {
                for (int col = 0; col < grid[row].length; col++) {
                    ans += Math.min(rowMin[row],colMin[col]) - grid[row][col];
                }
            }
            return ans;
        }
    }

    LeetCode刷题【670】最大交换

    给定一个非负整数,你至多可以交换一次数字中的任意两位。返回你能得到的最大值。

    示例 1 :

    输入: 2736
    输出: 7236
    解释: 交换数字2和数字7。
    

    示例 2 :

    输入: 9973
    输出: 9973
    解释: 不需要交换。
    

    注意:

    1. 给定数字的范围是 [0, 108]
    Related Topics
    • 贪心
    • 数学

  • 👍 244
  • 👎 0
  • 凑合写了个费劲巴拉的方法
    排序之后跟原序列对比,如果不同则交换位置

    交换的新数字取最右边出现的位置

    class Solution {
    
    
    
        public int maximumSwap(int num) {
            int size = stringSize(num);
            if (size==1){
                return num;
            }
            int[] arr = new int[size];
            int[] map = new int[10];
            Arrays.fill(map,-1);
            int idx = size;
            while ( num != 0){
                arr[--idx] = num%10;
                num /= 10;
                if (map[arr[idx]]==-1){
                    map[arr[idx]]=idx;
                }
            }
            PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
                @Override
                public int compare(int[] o1, int[] o2) {
                    return o2[0]-o1[0];
                }
            });
    
            for (int i = arr.length-1; i >=0; i--) {
                queue.offer(new int[]{arr[i],map[arr[i]]});
            }
    
            int i = -1;
            int ans = 0;
            while (!queue.isEmpty() && queue.peek()[0] == arr[++i]){
                ans = ans * 10 + queue.poll()[0];
            }
            if (queue.isEmpty()){
                return ans;
            }
            ans = ans * 10 + queue.peek()[0];
            int[] swapIdx = new int[]{i,queue.peek()[1]};
            while (++i < arr.length){
                if (i == swapIdx[1]){
                    ans = ans * 10 + arr[swapIdx[0]];
                }else{
                    ans = ans * 10 + arr[i];
                }
    
            }
    
            return ans;
        }
    
    
    
    
        final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                99999999, 999999999, Integer.MAX_VALUE };
    
        // Requires positive x
        static int stringSize(int x) {
            for (int i=0; ; i++)
                if (x <= sizeTable[i])
                    return i+1;
        }
    }

    LeetCode刷题【1005】K 次取反后最大化的数组和

    给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:

    • 选择某个下标 i 并将 nums[i] 替换为 -nums[i]

    重复这个过程恰好 k 次。可以多次选择同一个下标 i

    以这种方式修改数组后,返回数组 可能的最大和

     

    示例 1:

    输入:nums = [4,2,3], k = 1
    输出:5
    解释:选择下标 1 ,nums 变为 [4,-2,3] 。
    

    示例 2:

    输入:nums = [3,-1,0,2], k = 3
    输出:6
    解释:选择下标 (1, 2, 2) ,nums 变为 [3,1,0,2] 。
    

    示例 3:

    输入:nums = [2,-3,-1,5,-4], k = 2
    输出:13
    解释:选择下标 (1, 4) ,nums 变为 [2,3,-1,5,4] 。
    

     

    提示:

    • 1 <= nums.length <= 104
    • -100 <= nums[i] <= 100
    • 1 <= k <= 104
    Related Topics
    • 贪心
    • 数组
    • 排序

  • 👍 258
  • 👎 0
  • 马马虎虎优先队列

    class Solution {
        public int largestSumAfterKNegations(int[] nums, int k) {
            int sum = 0;
            PriorityQueue<Integer> queue = new PriorityQueue<>();
    
            int absMin = 101;
            for (int num : nums) {
                absMin = Math.min(absMin,Math.abs(num));
                if (num >= 0){
                    sum += num;
                }else{
                    queue.offer(num);
                }
            }
            if (queue.size()>0){
                //把绝对值较大的负数尽量翻转
                while (!queue.isEmpty() && k > 0){
                    sum -= queue.poll();
                    k--;
                }
                if (k==0){
                    //翻转次数用完了,还有数字没翻转
                    while (!queue.isEmpty()){
                        sum += queue.poll();
                    }
                    return sum;
                }
            }
            if (k%2 == 0){
                return sum;
            }else{
                return sum - 2*absMin;
            }
        }
    }

    LeetCode刷题【剑指 Offer 45】把数组排成最小的数

    输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

     

    示例 1:

    输入: [10,2]
    输出: "102"

    示例 2:

    输入: [3,30,34,5,9]
    输出: "3033459"

     

    提示:

    • 0 < nums.length <= 100

    说明:

    • 输出结果可能非常大,所以你需要返回一个字符串而不是整数
    • 拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0
    Related Topics
  • 贪心
  • 字符串
  • 排序

  • 👍 461
  • 👎 0
  • 愣了好久没看懂,反复思考下:堆排序

    class Solution {
        final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                99999999, 999999999, Integer.MAX_VALUE };
    
        public String minNumber(int[] nums) {
            PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    int size1 = stringSize(o1);
                    int size2 = stringSize(o2);
    
                    int join1 = o1;
                    while (size2>0){
                        join1 *= 10;
                        size2--;
                    }
                    join1 += o2;
                    int join2 = o2;
                    while (size1>0){
                        join2 *= 10;
                        size1--;
                    }
                    join2 += o1;
                    return join1 - join2;
                }
            });
            for (int num : nums) {
                queue.offer(num);
            }
            StringBuilder sb = new StringBuilder();
            while (!queue.isEmpty()){
                sb.append(queue.poll());
            }
            return sb.toString();
        }
    
    
        public int stringSize(int x) {
            for (int i=0; ; i++)
                if (x <= sizeTable[i])
                    return i+1;
        }
    }

    LeetCode刷题【763】划分字母区间

    字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

     

    示例:

    输入:S = "ababcbacadefegdehijhklij"
    输出:[9,7,8]
    解释:
    划分结果为 "ababcbaca", "defegde", "hijhklij"。
    每个字母最多出现在一个片段中。
    像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
    

     

    提示:

    • S的长度在[1, 500]之间。
    • S只包含小写字母 'a''z'
    Related Topics
  • 贪心
  • 哈希表
  • 双指针
  • 字符串

  • 👍 737
  • 👎 0
  • 基本思考思路及实现

    好像和官方题解想得一样,还是说下自己的思考过程吧

    1. 从前面找到一个字符,往后找到他最后出现的位置,这时候想到了可能需要双指针?或者从后往前找这个字符第一次出现的位置,不过好像有点复杂,
    2. 于是又想到了可以用哈希表记录下每个字符出现的最后位置,但是这时候又一个想法,是不是可以同时记下这个字符第一次出现的位置,这样就知道了这个字符需要对应的最小区间,但是如何和其他字符对应的区间是个问题,遂放弃,不过也许应该有办法
    3. 那么有了前面得到的最后位置之后我们就把找当前字符最后出现位置的操作变成了O(1)复杂度
    4. 继续下一个字符,判断他出现的最后位置,如果比之前那个小的话,就不用管,如果比之前那个大的话,说明这两个字符对应的最小区间需要扩大了
    5. 如果找到了某个字符出现最后位置就是当前对应的区间的结束位置的话,那么说明可以形成符合题意的区间了,记录长度
    class Solution {
        public List<Integer> partitionLabels(String s) {
            List<Integer> list = new ArrayList<>();
            int[] map = new int[26];
            int idx = -1;
            while (++idx < s.length()){
                map[s.charAt(idx) - 'a'] = idx;
            }
            int begin = 0;
            idx = -1;
            int lastPostion = -1;
            while (++idx < s.length()){
                lastPostion = Math.max(lastPostion,map[s.charAt(idx)-'a']);
                if (idx == lastPostion){
                    list.add(idx - begin + 1);
                    begin = idx+1;
                }
            }
            return list;
        }
    }

    LeetCode刷题【517】超级洗衣机

    假设有 n 台超级洗衣机放在同一排上。开始的时候,每台洗衣机内可能有一定量的衣服,也可能是空的。

    在每一步操作中,你可以选择任意 m (1 <= m <= n) 台洗衣机,与此同时将每台洗衣机的一件衣服送到相邻的一台洗衣机。

    给定一个整数数组 machines 代表从左至右每台洗衣机中的衣物数量,请给出能让所有洗衣机中剩下的衣物的数量相等的 最少的操作步数 。如果不能使每台洗衣机中衣物的数量相等,则返回 -1

     

    示例 1:

    输入:machines = [1,0,5]
    输出:3
    解释:
    第一步:    1     0 <-- 5    =>    1     1     4
    第二步:    1 <-- 1 <-- 4    =>    2     1     3    
    第三步:    2     1 <-- 3    =>    2     2     2   
    

    示例 2:

    输入:machines = [0,3,0]
    输出:2
    解释:
    第一步:    0 <-- 3     0    =>    1     2     0    
    第二步:    1     2 --> 0    =>    1     1     1     
    

    示例 3:

    输入:machines = [0,2,0]
    输出:-1
    解释:
    不可能让所有三个洗衣机同时剩下相同数量的衣物。
    

     

    提示:

    • n == machines.length
    • 1 <= n <= 104
    • 0 <= machines[i] <= 105
    Related Topics
  • 贪心
  • 数组

  • 👍 98
  • 👎 0
  • 有点绕。

    大意是,最大次数为要往某个区域内移入的最大次数,或者当前位置的洗衣机移出的最大次数,这两个值中较大的一个。

    class Solution {
        public int findMinMoves(int[] machines) {
            int sum = 0;
            int[] preSum = new int[machines.length+1];
            for (int i = 0; i < machines.length; i++) {
                preSum[i+1] = preSum[i] + machines[i];
            }
            sum = preSum[machines.length];
            if (sum % machines.length != 0){
                return -1;
            }
            int avg = sum/machines.length;
            int ans = 0;
            for (int i = 0; i < machines.length; i++) {
                ans = Math.max(ans,Math.abs(preSum[i] - avg * i));
                ans = Math.max(machines[i]-avg,ans);
            }
            return ans;
        }
    }

    LeetCode刷题【122】买卖股票的最佳时机 II

    给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。

    设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

    注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

     

    示例 1:

    输入: prices = [7,1,5,3,6,4]
    输出: 7
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
         随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
    

    示例 2:

    输入: prices = [1,2,3,4,5]
    输出: 4
    解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
         注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
    

    示例 3:

    输入: prices = [7,6,4,3,1]
    输出: 0
    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

     

    提示:

    • 1 <= prices.length <= 3 * 104
    • 0 <= prices[i] <= 104
    Related Topics
  • 贪心
  • 数组
  • 动态规划

  • 👍 1360
  • 👎 0
  • 动态规划,代码如下

    class Solution {
        public int maxProfit(int[] prices) {
            int[][] dp = new int[prices.length][2];
            dp[0][1] = -prices[0];
            for (int i = 1; i < dp.length; i++) {
                //如果今天不持有,则可能是昨天就不持有,或者把昨天持有的卖掉了赚到了prices[i]的钱
                dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
                //如果今天持有,可能是昨天就持有的,或者是昨天没持有今天买了  prices[i]的钱,需要减去这么多
                dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
            }
            return dp[dp.length-1][0];
        }
    }

    有点东西,一开始拎不清,题面中没说本金的事情然后就一直有点困扰,那么我们不妨假设一下,假设有本金100元。

    • 假设,第一天股价3元,买入了,此时状态为持有股票,手上本金100-3=97元
    • 假设,第二天股价是5元,卖出股票,获利5元,则手上持有97+5 = 102元,在此期间实际总获利102 – 100 = 2元。

    所以,我们可以看到,本金的多少其实并不重要。所以我们可以就一开始设定本金为0,最后可以直接不用再减一个0。

    那么如上面已经贴出来的代码那样:

    • 第n天假如手上不持有 ,则能获得的最大利润为 前一天也没有持有的时候的最大利润,或者前一天持有了,今天把手上的卖掉了获得的最大利润,两者取其中较大的 dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
    • 第n天假如手上持有了股票,则能获得的最大利润为,同样的前一天持有的时候能获得的最大利润,或者前一天不持有股票,但是今天购入股票的情况。这两者之间较大的 dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);

    优化,同样的dp[n]数组改成dp[2]

    class Solution {
        public int maxProfit(int[] prices) {
            int[][] dp = new int[2][2];
            dp[0][1] = -prices[0];
            int cur = 1,pre = 0;
            for (int num : prices) {
                //如果今天不持有,则可能是昨天就不持有,或者把昨天持有的卖掉了赚到了prices[i]的钱
                dp[cur][0] = Math.max(dp[pre][0],dp[pre][1]+num);
                //如果今天持有,可能是昨天就持有的,或者是昨天没持有今天买了  prices[i]的钱,需要减去这么多
                dp[cur][1] = Math.max(dp[pre][1],dp[pre][0]-num);
                pre = pre==1?0:1;
                cur = cur==1?0:1;
            }
            return dp[pre][0];
        }
    }

    另外,贪心的话。大概这样一个规则,只要今天的价格比昨天高,就今天卖掉,直接获取利润

    class Solution1 {
        public int maxProfit(int[] prices) {
            int profit = 0;
            for (int i = 1; i < prices.length; i++) {
                if (prices[i]>prices[i-1]){
                    profit += prices[i] - prices[i-1];
                }
            }
            return profit;
        }
    }