LeetCode2021春季编程大赛-个人赛

LeetCode2021春季编程大赛地址:https://leetcode-cn.com/contest/season/2021-spring/

第一题:采购方案

从N个零件里选2个,保证这两个零件花费和不超过预算,将N个零件从小到大进行排序,对于某个零件(下标为 i i i,花费 a a a),从它右端到结尾查找,找到最大花费 b b b的零件,使得 a + b ≤ t a r g e t a+b≤target a+btarget,如果 a ≥ t a r g e t a≥target atarget了,就不存在可能的组合了。
查找的过程使用二分查找提高效率,如果可以查找到返回满足条件的最大下标 j j j(所以这里用right_bound()方法查找满足条件的右边界),如果查找不到,返回-1。
对于某个a,假设存在一个b,使得 a + b ≤ t a r g e t a+b≤target a+btarget,那么可以选择a,再从 [ i + 1 , j ] [i+1, j] [i+1,j]里选择任意一个来组合,对于每个a,有 j − i j-i ji种组合,累加,最后取余。

class Solution {
    public int purchasePlans(int[] nums, int target) {
        int length = nums.length, MOD = 1000000007;
        Arrays.sort(nums);
        long result = 0;
        for (int i = 0; i < length; i++) {
            if (nums[i] >= target) {
                break;
            }
            int rightBound = right_bound(nums, target - nums[i], i + 1, length - 1);
            if (rightBound != -1) {
                result += rightBound - i;
            }
        }
        return (int) (result % MOD);
    }

    private int right_bound(int[] array, int key, int left, int right) {
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (array[mid] > key) {
                right = mid - 1;
            } else if (array[mid] < key) {
                left = mid + 1;
            } else {
                left = mid + 1;
            }
        }
        return right;
    }
}

第二题:乐团站位

这个题目道是不难,就是有点儿麻烦,首先一看数据量肯定不能模拟,要统计从左上角到要求点走过了多少个元素,再对9取余,从而判断[xPos, yPos]的值。
格外注意的一点是num的范围是 [ 1 , 1 0 9 ] [1, 10^{9}] [1,109],在计算的时候,很容易就爆int了,保险起见,全程用long处理。
在求外圈走了多少个数字的时候,直接用等差数列求和公式,不要for循环计算,非常耗时。

class Solution {
    // 注意爆int的问题,int*int的结果可能爆int
    public int orchestraLayout(int num, int xPos, int yPos) {
        long circle = Math.min(Math.min(xPos, num - xPos - 1), Math.min(yPos, num - yPos - 1));
        long total = 0;
        // 记录完整的外圈走了多少个数字(未优化:2782 ms)
        // for (int i = 0; i < circle; i++) {
        //     total += (num - (2 * i + 1)) * 4L;
        // }
        // 记录完整的外圈走了多少个数字(优化:0ms,直接用等差数列求解:(首项+末项)*项数/2)
        total = 4L * ((num - 1) + (num - (2 * (circle - 1) + 1))) * circle / 2L;
        if (xPos == circle) {// [xPos, yPos]位于圈的上侧
            total += yPos - circle + 1;
        } else if (yPos == num - circle - 1) {// [xPos, yPos]位于圈的右侧
            total += (num - (2 * circle + 1)) * 1L + (xPos - circle + 1);
        } else if (xPos == num - circle - 1) {// [xPos, yPos]位于圈的下侧
            total += (num - (2 * circle + 1)) * 2L + (num - circle - yPos);
        } else {// [xPos, yPos]位于圈的左侧
            total += (num - (2 * circle + 1)) * 3L + (num - circle - xPos);
        }
        total %= 9;
        if (total == 0) {
            total = 9;
        }
        return (int) total;
    }
}

第三题:魔塔游戏

先考虑-1的情况,遍历一遍求和sum,如果 s u m < 0 sum < 0 sum<0,那么就是-1。
用一个优先队列存储前面碰到的负值,当出现 b l o o d ≤ 0 blood≤0 blood0的时候,就从前面碰到的负值里,选择最小的一个进行恢复,恢复的目的在于让血量恢复的最多,也就是把这个扣最多的血量移动到最后。

class Solution {
    public int magicTower(int[] nums) {
        long blood = 1L;
        for (int num : nums) {
            blood += num;
        }
        if (blood < 0) {
            return -1;
        }
        blood = 1;
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        int operation = 0;
        for (int num : nums) {
            if (num < 0) {// 把负数放到优先队列里
                priorityQueue.offer(num);
                if (blood + num <= 0) {// 加上num后就死了
                    blood -= priorityQueue.poll();// 将前面碰到过的最小负数的扣血量恢复,也就是移动到最后
                    operation++;
                }
            }
            blood += num;
        }
        return operation;
    }
}

第四题:变换的迷宫

首先,将问题简化,考虑一个 m × n m \times n m×n的矩阵,其中有一些点是不可达的,问,从 [ 0 , 0 ] [0,0] [0,0]点走到 [ m − 1 , n − 1 ] [m-1,n-1] [m1,n1]点,是否存在一条路径可达。这时候还是比较简单的,可以用DFS暴搜。
加上一个时间维度,还是可以理解的,还可以暴力搜索,此时的时间复杂度是 O ( T M N ) O(TMN) O(TMN),然后考虑卷轴。
卷轴1是临时卷轴,使用一次就失效了,可以用0-1来表示使用状态。
卷轴2是永久卷轴,一旦使用,此位置始终是可达的,使用永久卷轴的目的是:将来某时刻会回到这个位置,这个操作可以等价为在使用永久卷轴位置停留了一段时间。
因此,使用临时卷轴的时候,只需要对下一时刻的DFS要将临时卷轴置1。使用永久卷轴的时候,需要从下一时刻到最终时刻的DFS将永久卷轴置1。
再加上一些剪枝操作进行优化。

class Solution {
    int[] dx = new int[]{0, 1, 0, -1, 0}, dy = new int[]{1, 0, -1, 0, 0};
    boolean[][][][][] visit;
    int tLimit, m, n;

    public boolean escapeMaze(List<List<String>> maze) {
        tLimit = maze.size() - 1;// t的取值范围是[0, maze.size() - 1]
        m = maze.get(0).size();// 矩阵的行数
        n = maze.get(0).get(0).length();// 矩阵的列数
        visit = new boolean[tLimit + 1][m][n][2][2];// 标记某种情况情况是否已经DFS探测过,避免重复探测
        return DFS(0, 0, 0, 0, 0, maze);
    }

    /**
     * @param t      时间维度
     * @param x      当前的为位置
     * @param y      当前的为位置
     * @param magic1 临时卷轴 0:未使用magic1;1:使用了magic1
     * @param magic2 永久卷轴 0:未使用magic2;1:使用了magic2
     * @param maze   迷宫
     */
    private boolean DFS(int t, int x, int y, int magic1, int magic2, List<List<String>> maze) {
        if ((m - 1 - x) + (n - 1 - y) > tLimit - t) {// [x, y]走到[m - 1, n - 1]最少需要的时间比剩余可用时间还多,说明走不到了
            return false;
        }
        if (visit[t][x][y][magic1][magic2]) {// 剪枝:当前情况已经考虑过了,不用继续对这种情况DFS了
            return false;
        }
        visit[t][x][y][magic1][magic2] = true;// 标记这种情况为考虑过
        if (x == m - 1 && y == n - 1) {// 走到了[m - 1, n - 1]的位置
            return true;
        }
        // 考虑下一个时刻的运动情况:4个方向和原地不动
        for (int i = 0; i < 5; i++) {
            int fx = x + dx[i], fy = y + dy[i];// 下一时刻新的位置
            if ((0 <= fx && fx <= m - 1) && (0 <= fy && fy <= n - 1)) {// 还在maze范围内
                char nextTimePosition = maze.get(t + 1).get(fx).charAt(fy);// 下一时刻的下一个位置的地形
                if (nextTimePosition == '.') {// 可达
                    // 如果DFS返回true,表示可达;如果DFS返回false,表示不可达,继续判断,这里我们只关心DFS返回true的情况
                    if (DFS(t + 1, fx, fy, magic1, magic2, maze)) {
                        return true;
                    }
                } else {// 不可达
                    if (magic1 == 0) {// 卷轴1还没使用
                        // 如果DFS返回true,表示可达;如果DFS返回false,表示不可达,继续判断,这里我们只关心DFS返回true的情况
                        if (DFS(t + 1, fx, fy, 1, magic2, maze)) {
                            return true;
                        }
                    }
                    if (magic2 == 0) {// 卷轴2还没使用
                        // 在[t + 1, tLimit]时刻里,用“卷轴2已使用”状态继续探测,有没有可能走到[m - 1, n - 1]位置
                        for (int temp = t + 1; temp <= tLimit; temp++) {
                            // 如果DFS返回true,表示可达;如果DFS返回false,表示不可达,继续判断,这里我们只关心DFS返回true的情况
                            if (DFS(temp, fx, fy, magic1, 1, maze)) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        return false;
    }
}

还看到一种比较好理解的方式,不过时间复杂度高,上面的方法,只要有一个满足要求的,就返回了,下面这种方式,会将整个图都跑一边遍,最后查找有没有满足条件的。

class Solution {
    int[] dx = new int[]{0, 1, 0, -1, 0}, dy = new int[]{1, 0, -1, 0, 0};
    boolean[][][][][] visit;
    int tLimit, m, n;

    public boolean escapeMaze(List<List<String>> maze) {
        tLimit = maze.size();// t的取值范围是[0, maze.size())
        m = maze.get(0).size();// 矩阵的行数
        n = maze.get(0).get(0).length();// 矩阵的列数
        visit = new boolean[tLimit][m][n][2][2];// 标记某种情况情况是否已经DFS探测过,避免重复探测
        visit[0][0][0][0][0] = true;// 初始位置访问过
        for (int t = 0; t + 1 < tLimit; t++) {// 遍历时刻
            for (int x = 0; x < m; x++) {// 遍历行
                for (int y = 0; y < n; y++) {// 遍历列
                    for (int i = 0; i < 5; i++) {// 考虑4个方向和原地不动
                        int fx = x + dx[i], fy = y + dy[i];// 新位置的坐标
                        if ((0 <= fx && fx < m) && (0 <= fy && fy < n)) {// 新位置没有出界
                            for (int magic1 = 0; magic1 < 2; magic1++) {// 遍历卷轴1
                                for (int magic2 = 0; magic2 < 2; magic2++) {// 遍历卷轴2
                                    // 剪枝:如果t时刻的[x, y]还没访问过,是不能访问t + 1时刻的[fx, fy]的
                                    if (!visit[t][x][y][magic1][magic2]) {
                                        continue;
                                    }
                                    // 探测t + 1时刻的[fx, fy]的情况
                                    detect(maze, t + 1, fx, fy, magic1, magic2);
                                }
                            }
                        }
                    }
                }
            }
        }
        // 探测过一遍后,从[0, tLimit)遍历t,关注[n - 1][m - 1]位置,有没有true的,如果有一个true的,说明可达
        for (int t = 0; t < tLimit; t++) {
            for (int i = 0; i < 2; i++) {
                for (int j = 0; j < 2; j++) {
                    if (visit[t][m - 1][n - 1][i][j]) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private void detect(List<List<String>> maze, int t, int x, int y, int magic1, int magic2) {
        char nextTimePosition = maze.get(t).get(x).charAt(y);// 下一时刻下一位置的地形
        if (nextTimePosition == '.') {// 可达
            visit[t][x][y][magic1][magic2] = true;
        } else {// 不可达
            if (magic1 == 0) {// 卷轴1还没用
                visit[t][x][y][1][magic2] = true;
            }
            if (magic2 == 0) {// 卷轴2还没用
                for (int i = t; i < tLimit; i++) {
                    visit[i][x][y][magic1][1] = true;
                }
            }
        }
    }
}

第五题:批量处理任务

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
国际程序设计大赛的作品欣赏 1、 先来第一个: 一段纯 3D 的 DOS 动画,据说是获一等奖的作品。虽然它不是最精美的, 但是当你得知它只有 4K 时,会不会立刻疯死掉呢? 附件:3ddemo.com 2、 再来一个: 幽灵古堡 farb-rausche 64.0 KB (65,536 字节) 恰好 65536 字节,显然是参赛作品。它非常漂亮(利用了 Direct3D),更让人惊奇的是只有 64K!而更让人震惊的是,如果不压缩的话它的数据大小是 1.6G!再体会一次“奇迹”! 附件:castle.exe 3、 再来一个: 死亡阴影 64.0 KB (65,536 字节) 附件:death.exe 4、 火域幻境 73.0 KB (74,752 字节) 虽然大小超过了 64K 的限制,但是它的效果可称为程序中的艺术品了! 附件:fire.exe 5、 fr-016 farb-rausche 16 字节 (16 字节) Let's rock hard!一个 DOS 里的小动画。看上去似乎没有什么特别,但是如果看到它的大小(16 字节),什么感觉????? 附件:fr-016.com 6、 第七天堂 Exceed 64.0 KB (65,536 字节) 由于参赛的要求是在 64K 之内即可,不少参赛者未免会有不到 65536 字节就有吃亏的感觉。 这是个 恰好 64K 的作品,可能利用了 DirectX 引擎,效果很好。 附件:heaven7.exe 7、 金属迷城 6.00 KB (6,144 字节) 考虑到它的大小时,你会不会体会到奇迹的含义 附件:metal.exe 8、 我要重点推荐的是这个作品fr-041_debris.exe(177K),效果是这所有作品之中最好的,一般的电脑无法流畅运行,我认为你买电脑时 可以把它带上运行一下作为一款测试工具。 附件:fr-041_debris.exe 9、 这个作品的效果和以上作品比都可名列前矛(64K),效果很好 附件:kkino64.exe 10、 这个就是传说中的25万倍压缩作品,prophecy《彗星撞地球》(63.5K)2000年时的最经典力作!画面看着挺舒服。 附件:prophecy《彗星撞地球》.exe 11、 爱之记忆 12、 3D裸女 13、 卡通 14、 光影 15、 FAiRLiGHT 这是在《三角洲3大地勇士》光碟版中带有的一个DEMO,发行组织FAiRLiGHT完全用原代码写出的自己组织的DEMO演示程序, 竟然才15K大小,画面也还行,对于他们的技术我们只能感到折服!
世界编程大赛第一名写的程序 汇编语言所写的。 这个程序是97年Mekka ’97 4K Intro比赛的一等奖作品,汇编语言所写。整个程序全长4095字节, 生成.com程序只有4K,可是却实现了3D动画的效果,还有一段背景音乐!画面是游戏天旋地转的一个场景! 1)把下面的代码粘贴到记事本里面,另存为 1.txt 文档。 2)在命令行窗口下(在开始→运行→cmd),进入文档所存盘符,输入debug<1.txt,就出现标准的三维空间动画和音乐了。悍啊.... 大家可以试一下,绝非病毒敬请放心。 附上完整的代码: -----------------------开始(请勿粘贴此行)--------------------------- e100 33 f6 bf 0 20 b5 10 f3 a5 8c c8 5 0 2 50 68 13 1 cb e 1f be a1 1 bf 0 1 e11b 6 57 b8 11 1 bb 21 13 89 7 4b 4b 48 79 f9 ad 86 e0 8b c8 bd ff ff e8 20 e134 0 3d 0 1 74 1a 7f 3 aa eb f3 2d ff 0 50 e8 f 0 5a f7 d8 8b d8 26 8a 1 aa e14f 4a 75 f9 eb de cb 57 bb 21 13 8b c1 40 f7 27 f7 f5 8b fb ba 11 1 4f 4f 4a e168 39 5 7f f9 52 8b c5 f7 25 f7 37 2b c8 95 f7 65 2 f7 37 95 2b e8 fe e fe e181 10 79 6 c6 6 fe 10 7 46 d0 14 d1 d1 d1 e5 79 ec 5a b8 11 1 ff 7 4b 4b 48 e19b 3b d0 75 f7 5f c3 83 f7 83 a6 5d 59 82 cd b2 8 42 46 9 57 a9 c5 ca aa 1b e1b4 4f 52 b4 92 3f ab 6e 9e a8 1d c6 3 fc e 6a e7 ae bb 5f 7b 10 b8 b4 f7 8 e1cd e2 bf 36 4e 39 9d 79 29 3f a f9 36 52 16 fb 5 e8 e5 a6 c2 e9 b0 43 d3 a3 e1e6 cf d3 fd fd cb d1 4c 5e e0 63 58 86 bb 3e 9 c1 20 bc cc 91 a3 47 81 70 b3 e1ff d6 1a 9e c2 c9 12 e7 4e ad f4 5f e3 30 e9 9 39 d7 e8 f9 f4 d2 44 e8 d7 22 e218 be e2 ce 88 25 cf 30 4a a8 29 ae 3f 47 c6 2d 85 e9 73 54 13 b e6 e0 34 65 e231 e2 50 8a 89 18 5f ce 70 99 3 5f 42 bf eb 7 ae d0 ca 5 22 8d 22 a5 b7 f0 e24a 90 81 bc 7a bc dc 5 db c0 6a 2 e5 57 38 be 60 cb ac ba a5 3b 9d f1 77 38 e263 a6 84 d1 3c af 49 d8 6a 45 a2 76 60 21 12 c0 c2 44 f2 5e bb e5 37 a9 2b e27b ec 4a 8c 4c f2 f7 a9 58 71 2b ba 6d d6 6a e5 60 46 e0 da e5 b9 90 e5 a3 e293 f7 7f 31 60 58 f0 c4 88 10 4e 3c a3 ee 4e 11 55 8f a 92 eb db ad 7a 9c f e2ac db 5a 28 96 da 87 ae 91 91 2d e3 5e ea df 6 95 71 67 71 40 ce d1 2e 31 6d e2c5 c1 9c d8 6a 76 9b 4a e8 36 44 d6 76 d 30 5 ff d4 1b ac 1f 32 65 31 bf 55 e2de 26 b a4 55 e1 5d 5e 16 ed 97 48 6c 77 fb 81 86 e f9 18 bd d4 f4 8b de 1d e2f7 ba d 47 75 3 89 4b 3e dc 27 86 1c d0 17 89 48 d1 a6 8d d4 2b 54 4e 8f b0 e310 2 e1 6b 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值