├── Anniversary party.java ├── Beautiful Numbers ├── B-large-practice.in ├── B-large-practice.out ├── B-small-practice.in ├── B-small-practice.out ├── Solution.class └── Solution.java ├── Counting Islands II.java ├── Email Merge.java ├── Find the Maximum Subarray XOR in an Array.java ├── Game of Connections.java ├── Get All Possible Binary Trees.java ├── HihoCoder_1722 最小差值.java ├── Maximizing XOR.java ├── Maximum XOR Subset.java ├── POJ 2420 A Star not a Tree.java ├── POJ-1160 Post Office.java ├── POJ-2976 Dropping tests.java ├── Path in Matrix.java ├── Random Pool(随机池).java ├── Reverse a stack using recursion.java ├── Swap Two Integers.java ├── Tencent_微信红包.java ├── Tencent_游戏任务标记.java ├── Tencent_素数对.java ├── n个数里最小的k个.java ├── 一笔画.java ├── 两个有序数组相加和的TopK.java ├── 二分法求局部最小值.java ├── 京东_保卫方案.java ├── 京东_幸运数.java ├── 京东_神奇数.java ├── 京东_进制均值.java ├── 今日头条_手串.java ├── 今日头条_用户喜好.java ├── 任意点.java ├── 修补木桶.java ├── 分数调查.java ├── 分金条的最少花费.java ├── 删数.java ├── 删除元素构造回文串的最小代价.java ├── 判断是否为完全二叉树.java ├── 双拆分数.java ├── 合唱团.java ├── 含有连续两个str作为子串的最短字符串.java ├── 吸血鬼数.java ├── 大数据里找上中位数.java ├── 奇虎360_偶串.java ├── 奇虎360_剪气球串.java ├── 奇虎360_跑步.java ├── 字符串中英文字符的个数.java ├── 字符串的交错组成.java ├── 寻宝.java ├── 小Ho的防护盾.java ├── 小易喜欢的数列.java ├── 岛屿个数.java ├── 异或和为0的子数组个数.java ├── 循环单词.java ├── 招银科技_整数成绩最大化.java ├── 拼多多_迷宫寻路.java ├── 换钱的最少货币数.java ├── 换零钱.java ├── 插入元素构造回文数组的最小代价.java ├── 操作序列.java ├── 数字在排序数组中出现的次数.java ├── 数组中各个元素出现的次数.java ├── 数组和为sum的方法数.java ├── 数组的小和(单调和).java ├── 明明的随机数.java ├── 最多的会议场数.java ├── 最大值减去最小值小于等于k的子数组数量.java ├── 最大子矩阵和.java ├── 最小数字乘以区间和的最大值.java ├── 最小编辑代价.java ├── 最长01子串.java ├── 有趣的数字.java ├── 树的子结构.java ├── 求出现次数的TopK问题.java ├── 汽水瓶.java ├── 添加回文串.java ├── 滴滴_餐馆.java ├── 牛牛取快递.java ├── 牛牛吃雪糕.java ├── 牛牛打响指.java ├── 牛牛的数列.java ├── 牛牛的背包问题.java ├── 畅通工程.java ├── 筛法求素数.java ├── 简单计算器.java ├── 累加和小于等于K的最长子数组.java ├── 纸牌博弈.java ├── 线段相交点个数.java ├── 网易-骰子游戏.java ├── 网易_回文序列.java ├── 网易_地牢逃脱.java ├── 网易_字符串编码.java ├── 网易_安置路灯.java ├── 网易_幸运的袋子.java ├── 网易_推箱子.java ├── 网易_数列还原.java ├── 网易_数字游戏.java ├── 网易_数对.java ├── 网易_星际穿越.java ├── 网易_最大和.java ├── 网易_混合颜料.java ├── 网易_牛牛找工作.java ├── 网易_牛牛的闹钟.java ├── 网易_矩形重叠.java ├── 网易_被3整除.java ├── 网易_解救小易.java ├── 网易_赛马.java ├── 网易_路灯.java ├── 网易_迷路的牛牛.java ├── 网易_饥饿的小易.java ├── 网易有道_洗牌.java ├── 美丽的项链.java ├── 美团_丢失的三个数.java ├── 能转换的最长字串(仅行字符a,b).java ├── 腾讯_小Q的歌单.java ├── 腾讯_翻转数列.java ├── 腾讯_贪吃的小Q.java ├── 蘑菇街_回文串.java ├── 蘑菇街_聊天.java ├── 调整数组顺序使奇数位于偶数前面.java ├── 购物单.java ├── 进制转换.java ├── 需要排序的最短子数组长度.java ├── 魔法币.java └── 鹰蛋问题.java /Beautiful Numbers/B-large-practice.in: -------------------------------------------------------------------------------- 1 | 100 2 | 14919921443713777 3 | 16035713712910627 4 | 470988884881403701 5 | 663208457429249320 6 | 35417244247309081 7 | 18912140357306197 8 | 821424692950225218 9 | 194148123592167400 10 | 169683965173070355 11 | 30896151154490161 12 | 8152332946963171 13 | 26504196177401065 14 | 727004545306745403 15 | 1000000000000000000 16 | 210724372007205925 17 | 230839591399678411 18 | 139782788180007861 19 | 31108559476176585 20 | 70640308161049884 21 | 919818571896748567 22 | 805068181058041870 23 | 528077080918778560 24 | 3392264354546656 25 | 172589956609631527 26 | 752116768933572174 27 | 278385972618095971 28 | 500364605967785712 29 | 936501597464173890 30 | 5346848976102943 31 | 42711799474754936 32 | 39739406344976521 33 | 96642126154887385 34 | 507373618559718949 35 | 148522861921131118 36 | 5990709545080807 37 | 58873926717610501 38 | 779668738937986840 39 | 34824506845925071 40 | 131226544799105148 41 | 14500751095055161 42 | 541064907662839644 43 | 665930169964312773 44 | 2854907964745564 45 | 151970818238211610 46 | 916365355264637559 47 | 353896329635329936 48 | 625318600331879360 49 | 690683275350237133 50 | 31899883679471306 51 | 99670604181093725 52 | 736827088955709913 53 | 113836064401097977 54 | 8508110325218143 55 | 13321050162245608 56 | 609649911611389831 57 | 46268622795238201 58 | 468339557884957084 59 | 25965247599543841 60 | 381252133354068211 61 | 494196153725539231 62 | 945648548682242665 63 | 19337682799995045 64 | 619861907468053901 65 | 229239004074282805 66 | 386581418747680 67 | 610260633162639678 68 | 299694296458966143 69 | 845078653019833051 70 | 373345485670171062 71 | 27233228344819564 72 | 129023667184858285 73 | 83124300916686757 74 | 379255974823102021 75 | 245249591795535097 76 | 43969534069696 77 | 66722517469920 78 | 9858438270839893 79 | 6910943299863520 80 | 25717131834499624 81 | 92023103018361641 82 | 943990493461794871 83 | 513767149194597040 84 | 112064524403878803 85 | 174210760090244515 86 | 5811353213141560 87 | 85718519097865885 88 | 984245729893369243 89 | 516227865362638410 90 | 106954294285992115 91 | 58860842632125700 92 | 446401559469368954 93 | 900075818947323940 94 | 813918209914834753 95 | 803117911029730313 96 | 130463899351376137 97 | 6980452392892987 98 | 66613711011280280 99 | 311119354403376793 100 | 114349900145584129 101 | 182859777940000980 102 | -------------------------------------------------------------------------------- /Beautiful Numbers/B-large-practice.out: -------------------------------------------------------------------------------- 1 | Case #1: 496 2 | Case #2: 502 3 | Case #3: 686286299 4 | Case #4: 872067 5 | Case #5: 117 6 | Case #6: 516 7 | Case #7: 821424692950225217 8 | Case #8: 579043 9 | Case #9: 553622 10 | Case #10: 560 11 | Case #11: 90290270 12 | Case #12: 298152 13 | Case #13: 727004545306745402 14 | Case #14: 999999999999999999 15 | Case #15: 84 16 | Case #16: 480457689 17 | Case #17: 373875364 18 | Case #18: 314504 19 | Case #19: 413381 20 | Case #20: 986 21 | Case #21: 805068181058041869 22 | Case #22: 808287 23 | Case #23: 150255 24 | Case #24: 746 25 | Case #25: 752116768933572173 26 | Case #26: 527622945 27 | Case #27: 500364605967785711 28 | Case #28: 936501597464173889 29 | Case #29: 418 30 | Case #30: 42711799474754935 31 | Case #31: 584 32 | Case #32: 458904 33 | Case #33: 507373618559718948 34 | Case #34: 148522861921131117 35 | Case #35: 426 36 | Case #36: 242639499 37 | Case #37: 779668738937986839 38 | Case #38: 45 39 | Case #39: 131226544799105147 40 | Case #40: 120419064 41 | Case #41: 541064907662839643 42 | Case #42: 665930169964312772 43 | Case #43: 141861 44 | Case #44: 81 45 | Case #45: 971306 46 | Case #46: 707335 47 | Case #47: 625318600331879359 48 | Case #48: 690683275350237132 49 | Case #49: 31899883679471305 50 | Case #50: 268 51 | Case #51: 858386328 52 | Case #52: 696 53 | Case #53: 92239418 54 | Case #54: 201 55 | Case #55: 847930 56 | Case #56: 599 57 | Case #57: 776581 58 | Case #58: 544 59 | Case #59: 725110 60 | Case #60: 889 61 | Case #61: 981544 62 | Case #62: 212 63 | Case #63: 619861907468053900 64 | Case #64: 21881 65 | Case #65: 72847 66 | Case #66: 610260633162639677 67 | Case #67: 547443418 68 | Case #68: 174 69 | Case #69: 373345485670171061 70 | Case #70: 300861 71 | Case #71: 505308 72 | Case #72: 288312852 73 | Case #73: 615837620 74 | Case #74: 791 75 | Case #75: 35295 76 | Case #76: 40559 77 | Case #77: 99289668 78 | Case #78: 183 79 | Case #79: 295171 80 | Case #80: 92023103018361640 81 | Case #81: 980970 82 | Case #82: 800919 83 | Case #83: 334760398 84 | Case #84: 558502 85 | Case #85: 179787 86 | Case #86: 76 87 | Case #87: 992091593 88 | Case #88: 516227865362638409 89 | Case #89: 474678 90 | Case #90: 388993 91 | Case #91: 446401559469368953 92 | Case #92: 900075818947323939 93 | Case #93: 31 94 | Case #94: 803117911029730312 95 | Case #95: 712 96 | Case #96: 437 97 | Case #97: 253 98 | Case #98: 823 99 | Case #99: 485376 100 | Case #100: 37 101 | -------------------------------------------------------------------------------- /Beautiful Numbers/B-small-practice.in: -------------------------------------------------------------------------------- 1 | 100 2 | 625 3 | 93 4 | 108 5 | 975 6 | 863 7 | 617 8 | 856 9 | 75 10 | 538 11 | 518 12 | 125 13 | 888 14 | 687 15 | 638 16 | 485 17 | 646 18 | 325 19 | 528 20 | 598 21 | 575 22 | 375 23 | 417 24 | 606 25 | 803 26 | 434 27 | 384 28 | 915 29 | 223 30 | 1000 31 | 798 32 | 365 33 | 318 34 | 135 35 | 828 36 | 183 37 | 968 38 | 676 39 | 924 40 | 777 41 | 217 42 | 206 43 | 25 44 | 746 45 | 908 46 | 358 47 | 737 48 | 63 49 | 146 50 | 403 51 | 45 52 | 587 53 | 345 54 | 197 55 | 727 56 | 447 57 | 14 58 | 395 59 | 283 60 | 266 61 | 38 62 | 334 63 | 466 64 | 955 65 | 875 66 | 427 67 | 478 68 | 543 69 | 175 70 | 695 71 | 494 72 | 248 73 | 766 74 | 818 75 | 983 76 | 157 77 | 7 78 | 943 79 | 838 80 | 258 81 | 556 82 | 504 83 | 783 84 | 116 85 | 308 86 | 934 87 | 234 88 | 847 89 | 717 90 | 567 91 | 657 92 | 665 93 | 454 94 | 163 95 | 703 96 | 294 97 | 276 98 | 898 99 | 55 100 | 756 101 | 83 102 | -------------------------------------------------------------------------------- /Beautiful Numbers/B-small-practice.out: -------------------------------------------------------------------------------- 1 | Case #1: 624 2 | Case #2: 92 3 | Case #3: 107 4 | Case #4: 974 5 | Case #5: 862 6 | Case #6: 616 7 | Case #7: 855 8 | Case #8: 74 9 | Case #9: 537 10 | Case #10: 517 11 | Case #11: 124 12 | Case #12: 887 13 | Case #13: 686 14 | Case #14: 637 15 | Case #15: 484 16 | Case #16: 645 17 | Case #17: 324 18 | Case #18: 527 19 | Case #19: 597 20 | Case #20: 574 21 | Case #21: 374 22 | Case #22: 416 23 | Case #23: 605 24 | Case #24: 802 25 | Case #25: 433 26 | Case #26: 383 27 | Case #27: 914 28 | Case #28: 222 29 | Case #29: 999 30 | Case #30: 797 31 | Case #31: 364 32 | Case #32: 317 33 | Case #33: 134 34 | Case #34: 827 35 | Case #35: 13 36 | Case #36: 967 37 | Case #37: 675 38 | Case #38: 923 39 | Case #39: 776 40 | Case #40: 216 41 | Case #41: 205 42 | Case #42: 24 43 | Case #43: 745 44 | Case #44: 907 45 | Case #45: 357 46 | Case #46: 736 47 | Case #47: 2 48 | Case #48: 145 49 | Case #49: 402 50 | Case #50: 44 51 | Case #51: 586 52 | Case #52: 344 53 | Case #53: 196 54 | Case #54: 726 55 | Case #55: 446 56 | Case #56: 13 57 | Case #57: 394 58 | Case #58: 282 59 | Case #59: 265 60 | Case #60: 37 61 | Case #61: 333 62 | Case #62: 465 63 | Case #63: 954 64 | Case #64: 874 65 | Case #65: 426 66 | Case #66: 477 67 | Case #67: 542 68 | Case #68: 174 69 | Case #69: 694 70 | Case #70: 493 71 | Case #71: 247 72 | Case #72: 765 73 | Case #73: 817 74 | Case #74: 982 75 | Case #75: 12 76 | Case #76: 2 77 | Case #77: 942 78 | Case #78: 837 79 | Case #79: 257 80 | Case #80: 555 81 | Case #81: 503 82 | Case #82: 782 83 | Case #83: 115 84 | Case #84: 307 85 | Case #85: 933 86 | Case #86: 233 87 | Case #87: 846 88 | Case #88: 716 89 | Case #89: 566 90 | Case #90: 656 91 | Case #91: 664 92 | Case #92: 453 93 | Case #93: 162 94 | Case #94: 26 95 | Case #95: 293 96 | Case #96: 275 97 | Case #97: 897 98 | Case #98: 54 99 | Case #99: 755 100 | Case #100: 82 101 | -------------------------------------------------------------------------------- /Beautiful Numbers/Solution.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/Beautiful Numbers/Solution.class -------------------------------------------------------------------------------- /Counting Islands II.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目: Counting Islands II 3 | 时间限制:10000ms 4 | 单点时限:1000ms 5 | 内存限制:256MB 6 | 描述 7 | Country H is going to carry out a huge artificial islands project. 8 | The project region is divided into a 1000x1000 grid. The whole project will last for N weeks. 9 | Each week one unit area of sea will be filled with land. 10 | 11 | As a result, new islands (an island consists of all connected land in 4 -- up, down, left and right -- directions) emerges in this region. 12 | Suppose the coordinates of the filled units are (0, 0), (1, 1), (1, 0). Then after the first week there is one island: 13 | #... 14 | .... 15 | .... 16 | .... 17 | After the second week there are two islands: 18 | #... 19 | .#.. 20 | .... 21 | .... 22 | After the three week the two previous islands are connected by the newly filled land and thus merge into one bigger island: 23 | #... 24 | ##.. 25 | .... 26 | .... 27 | Your task is track the number of islands after each week's land filling. 28 | 29 | 输入 30 | The first line contains an integer N denoting the number of weeks. (1 ≤ N ≤ 100000) 31 | Each of the following N lines contains two integer x and y denoting the coordinates of the filled area. (0 ≤ x, y < 1000) 32 | 33 | 输出 34 | For each week output the number of islands after that week's land filling. 35 | 36 | 样例输入 37 | 3 38 | 0 0 39 | 1 1 40 | 1 0 41 | 样例输出 42 | 1 43 | 2 44 | 1 45 | 46 | OJ地址:https://hihocoder.com/contest/hiho183/problem/1 47 | */ 48 | 49 | /** 50 | * Approach: Union Find 51 | * LeetCode上的原题...没啥好说的了... 52 | * 详细解释可以参见:(有 HashMap 和 Array 两个版本) 53 | * https://github.com/cherryljr/LeetCode/blob/master/Number%20of%20Islands%20II.java 54 | */ 55 | 56 | import java.util.*; 57 | 58 | public class Main { 59 | static int[] uf = new int[1000000]; 60 | static int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; 61 | static int count = 0; 62 | 63 | public static void main(String[] args) { 64 | Arrays.fill(uf, -1); 65 | Scanner sc = new Scanner(System.in); 66 | int n = sc.nextInt(); 67 | int x, y, index; 68 | for (int i = 0; i < n; i++) { 69 | x = sc.nextInt(); 70 | y = sc.nextInt(); 71 | index = x * 1000 + y; 72 | uf[index] = index; 73 | count++; 74 | 75 | for (int[] dir : dirs) { 76 | int nextX = x + dir[0]; 77 | int nextY = y + dir[1]; 78 | int nextP = nextX * 1000 + nextY; 79 | if (nextX < 0 || nextX >= 1000 || nextY < 0 || nextY >= 1000 || uf[nextP] == -1) { 80 | continue; 81 | } 82 | 83 | int rootA = findFather(index); 84 | int rootB = findFather(nextP); 85 | if (rootA != rootB) { 86 | uf[rootA] = rootB; 87 | count--; 88 | } 89 | } 90 | System.out.println(count); 91 | } 92 | } 93 | 94 | private static int findFather(int index) { 95 | while (index != uf[index]) { 96 | uf[index] = uf[uf[index]]; 97 | index = uf[index]; 98 | } 99 | return index; 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /Find the Maximum Subarray XOR in an Array.java: -------------------------------------------------------------------------------- 1 | /* 2 | Given an array of integers. find the maximum XOR subarray value in given array. 3 | Expected time complexity O(n). 4 | 5 | Examples: 6 | Input: arr[] = {1, 2, 3, 4} 7 | Output: 7 8 | The subarray {3, 4} has maximum XOR value 9 | 10 | Input: arr[] = {8, 1, 2, 12, 7, 6} 11 | Output: 15 12 | The subarray {1, 2, 12} has maximum XOR value 13 | 14 | Input: arr[] = {4, 6} 15 | Output: 6 16 | The subarray {6} has maximum XOR value 17 | */ 18 | 19 | /** 20 | * Approach: Trie 21 | * 对于 Subarray 问题,我们可以考虑 PreSum, Two Pointer, DP 等方法, 22 | * 经过分析,我们发现也就只有 PreXOR(EOR) 的方法可以用一用。 23 | * 总体思路为:求所有以 i 作为结尾的 最大异或 子数组。那么答案必定在这里面。 24 | * 但是我们发现在本题中无法利用 HashMap 实现加速。(因为我们没办法通过它来求得 最大异或和) 25 | * 因此我们需要考虑另外的一种数据机构来帮我们实现加速。而这里使用的就是 Trie Tree. 26 | * 27 | * 我们在 Trie Tree 中放入了放入了 从0到i 的异或值 EOR. 28 | * 那么根据 异或运算 的性质: A^B=C => B^C=A 29 | * 我们可以通过 0...i 的异或和计算出 任意 j...i 的异或值: 30 | * XOR[j...i] = XOR[0...i] ^ XOR[0...j-1] 31 | * 32 | * 那么Trie如何实现快速得到 异或后的最大值 呢?(本题的核心部分) 33 | * 我们是按照这样的策略(其实是一个贪心的做法): 34 | * 对于一个 int 类型,总共有 32 位。因此我们 Trie Tree 的高度为 32. 35 | * 当我们想要查询如何获得最大的异或值时, 36 | * 我们可以 从高位开始 向低位逐位查找 最佳异或值。遵循着 首先满足高位 的做法依次向下查询。 37 | * 同时:我们还需要注意符号位,这里比较特殊,因为 0 代表正,1代表负。 38 | * 所以对于 符号位 我们要取与原来位置上 相同 的数, 39 | * 而对于其他位我们要去与原来位置上 不同的数(异或1后的数)。 40 | * 如果 取不到 最佳值的话,我们就只能取现有的值了(总共就两个...) 41 | * 最后假设我们得到的 最大异或和 为:EOR[x]^EOR[y],那么就说明 最大异或和子数组为:[x+1...y] 42 | * 这样查找下来我们需要循环 32 次,时间复杂度为 O(1) 43 | * 而我们需要查找 n 个数,因此总体时间复杂度为 O(n) 44 | */ 45 | class Solution { 46 | public int maxSubarrayXOR(int[] nums) { 47 | if (nums == null || nums.length == 0) { 48 | return 0; 49 | } 50 | 51 | Trie trie = new Trie(); 52 | // 初始化 trie,将 0 将入到 trie 中,因为 X^0=X 53 | trie.add(0); 54 | int eor = 0; 55 | int max = Integer.MIN_VALUE; 56 | for (int i = 0; i < nums.length; i++) { 57 | eor ^= nums[i]; 58 | // 在 Trie 中寻找与 eor 异或起来最大的元素,返回异或之后的结果 59 | max = Math.max(max, trie.maxXOR(eor)); 60 | // 将 eor 加入到 Trie Tree 中 61 | trie.add(eor); 62 | } 63 | 64 | return max; 65 | } 66 | 67 | class TrieNode { 68 | // Trie Tree 的节点,因为是 2进制,所以我们只需要 0,1 两个节点即可 69 | TrieNode[] child; 70 | 71 | TrieNode() { 72 | child = new TrieNode[2]; 73 | } 74 | } 75 | 76 | class Trie { 77 | TrieNode root; 78 | 79 | Trie() { 80 | root = new TrieNode(); 81 | } 82 | 83 | // 将 nums[i] 加入到 Trie Tree 中 84 | public void add(int num) { 85 | TrieNode curr = root; 86 | for (int i = 31; i >= 0; i--) { 87 | int path = ((num >> i) & 1); // 获得第 i 位上的值 88 | if (curr.child[path] == null) { 89 | curr.child[path] = new TrieNode(); 90 | } 91 | curr = curr.child[path]; 92 | } 93 | } 94 | 95 | public int maxXOR(int num) { 96 | TrieNode curr = root; 97 | int rst = 0; 98 | for (int i = 31; i >= 0; i--) { 99 | // 获得第 i 位上的值 100 | int path = ((num >> i) & 1); 101 | // 根据 path,我们需要得到相应的最佳 异或值 102 | // 注意:如果是 符号位 的话,我们取与path相同的值,否则取与 path 不同的值 103 | int best = i == 31 ? path : (path ^ 1); 104 | // 判断 curr.child[best] 节点是否存在,存在的话我们就能取到最佳值 105 | // 否则只能取另外一个值了 106 | best = curr.child[best] != null ? best : (best ^ 1); 107 | // 将 rst 的第 i 位置为 path^best 108 | rst |= ((path ^ best) << i); 109 | curr = curr.child[best]; 110 | } 111 | return rst; 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /Game of Connections.java: -------------------------------------------------------------------------------- 1 | /* 2 | Description 3 | This is a small but ancient game. 4 | You are supposed to write down the numbers 1, 2, 3, . . . , 2n - 1, 2n consecutively in clockwise order 5 | on the ground to form a circle, and then, 6 | to draw some straight line segments to connect them into number pairs. 7 | Every number must be connected to exactly one another. 8 | And, no two segments are allowed to intersect. 9 | It's still a simple game, isn't it? But after you've written down the 2n numbers, 10 | can you tell me in how many different ways can you connect the numbers into pairs? Life is harder, right? 11 | 12 | Input 13 | Each line of the input file will be a single positive number n, except the last line, which is a number -1. 14 | You may assume that 1 <= n <= 100. 15 | 16 | Output 17 | For each n, print in a single line the number of ways to connect the 2n numbers into pairs. 18 | 19 | Sample Input 20 | 2 21 | 3 22 | -1 23 | Sample Output 24 | 2 25 | 5 26 | */ 27 | 28 | /** 29 | * Approach: Catalan Number 30 | * 这道题目与 求n个结点可构造多少种不同的二叉树 相同,都是 卡特兰数 的应用。 31 | * 因为是高精度,所以我们需要使用到 BigInteger 这个类来帮助我们解决问题。 32 | * 33 | * 卡特兰数的代码可以使用 递归 求解,但是我们都知道 卡特兰数 有几种求解方式, 34 | * 我们应该使用哪一种最合适呢? 35 | * 首先,我们可以令 h(0)=1,h(1)=1,catalan数满足递归式: 36 | * h(n)= h(0)*h(n-1) + h(1)*h(n-2) + ... + h(n-1)h(0) (其中n>=2),这是n阶递推关系; 37 | * 并且可以进一步化简为1阶递推关系: 38 | * h(n) = (4n-2)/(n+1)*h(n-1) (n>1) h(0)=1 39 | * 毫无疑问,这个一阶递推式就是我们需要的 40 | * 该递推关系的解为:h(n)=C(2n,n)/(n+1) (n=1,2,3,...) 41 | * 卡 塔兰数例的前几项为[注: n = 0, 1, 2, 3, … n] 42 | * 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 43 | * 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020,... 44 | * 45 | * 我们只需要建立一个 BigInteger[] 用来存储递归计算出来的结果即可。(打表) 46 | */ 47 | 48 | import java.io.BufferedInputStream; 49 | import java.math.*; 50 | import java.util.*; 51 | 52 | public class Main { 53 | public static void main(String[] args) { 54 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 55 | BigInteger[] table = new BigInteger[105]; 56 | 57 | table[0] = BigInteger.valueOf(1); 58 | table[1] = BigInteger.valueOf(1); 59 | for (int i = 2; i <= 100; i++) { 60 | // 要注意 (4n-2)/(n+1) 可能为小数 61 | table[i] = table[i - 1].multiply(BigInteger.valueOf(4 * i - 2)).divide(BigInteger.valueOf(i + 1)); 62 | } 63 | 64 | while (sc.hasNext()) { 65 | int n = sc.nextInt(); 66 | if (n == -1) { 67 | break; 68 | } 69 | System.out.println(table[n]); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /HihoCoder_1722 最小差值.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:10000ms 3 | 单点时限:1000ms 4 | 内存限制:256MB 5 | 6 | 描述 7 | 给定N个数组,每个数组都包含M个整数。 8 | 现在你被要求从每个数组中选出一个数,总共N个数,然后求出其中最大与最小的差值。 9 | 在MN种选法中,差值最小是多少? 10 | 11 | 输入 12 | 第一行包含两个整数N和M。 13 | 以下N行,每行包含M个整数。 14 | 对于50%的数据,1 ≤ N × M ≤ 10000 15 | 对于100%的数据,1 ≤ N × M ≤ 200000 0 ≤ 每个整数 ≤ 1000000 16 | 17 | 输出 18 | 最小的差值 19 | 20 | 样例输入 21 | 3 3 22 | 8 1 6 23 | 3 5 7 24 | 4 9 2 25 | 26 | 样例输出 27 | 2 28 | */ 29 | 30 | /** 31 | * Approach: PriorityQueue 32 | * 属于 Merge k Sorted Arrays 的 Fellow Up. 难度上并没有太大的提升. 33 | * 首先我们需要将每个数组进行一次排序(从小到大) 34 | * 然后与 Merge k Sorted Arrays 做法相同, 35 | * 利用每个数组的最小值(每行)来初始化优先级队列(最小堆) 36 | * 然后每次从 pq 里 poll 出最小值,求最小差值。 37 | * 并且如果当前值后面还有元素就可以将后续元素继续添加到队列中。 38 | * 对此我们需要保存该元素在矩阵中的坐标位置。 39 | * 40 | * 时间复杂度:O(NlogM) 41 | * 空间复杂度:O(N) 42 | * 43 | * Merge k Sorted Arrays: 44 | * https://github.com/cherryljr/LintCode/blob/master/Merge%20k%20Sorted%20Arrays.java 45 | */ 46 | 47 | import java.util.*; 48 | 49 | public class Main { 50 | public static void main(String[] args) { 51 | Scanner sc = new Scanner(System.in); 52 | int M = sc.nextInt(); 53 | int N = sc.nextInt(); 54 | int[][] matrix = new int[M][N]; 55 | for (int i = 0; i < M; i++) { 56 | for (int j = 0; j < N; j++) { 57 | matrix[i][j] = sc.nextInt(); 58 | } 59 | } 60 | 61 | PriorityQueue pq = new PriorityQueue<>(); 62 | int max = Integer.MIN_VALUE; 63 | int minDiff = Integer.MAX_VALUE; 64 | for (int i = 0; i < M; i++) { 65 | // 对每行数组进行一次排序 66 | Arrays.sort(matrix[i]); 67 | // 求初始化时的最大值(最小值可以通过优先级队列来获取) 68 | max = Math.max(max, matrix[i][0]); 69 | pq.offer(new Node(matrix[i][0], i, 0)); 70 | } 71 | 72 | while (!pq.isEmpty()) { 73 | // 获取当前所选数组中的最小值 74 | Node curr = pq.poll(); 75 | // Update minDiff 76 | minDiff = Math.min(minDiff, max - curr.value); 77 | if (curr.col < N - 1) { 78 | int nextValue = matrix[curr.row][curr.col + 1]; 79 | max = Math.max(max, nextValue); 80 | // 将当前元素所属数组中的下一个元素添加到队列中 81 | pq.offer(new Node(nextValue, curr.row, curr.col + 1)); 82 | } else { 83 | // 如果没有后续可以添加的元素,直接退出即可 84 | break; 85 | } 86 | } 87 | System.out.println(minDiff); 88 | } 89 | 90 | static class Node implements Comparable { 91 | int row, col; 92 | int value; 93 | 94 | public Node(int value, int row, int col) { 95 | this.value = value; 96 | this.row = row; 97 | this.col = col; 98 | } 99 | 100 | @Override 101 | public int compareTo(Node other) { 102 | return this.value - other.value; 103 | } 104 | 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /Maximizing XOR.java: -------------------------------------------------------------------------------- 1 | /* 2 | 给定两个整数:L 和 R 3 | L ≤ A ≤ B ≤ R, 找出 A xor B 的最大值。 4 | 5 | 输入格式 6 | 第一行为 L 7 | 第二行为 R 8 | 9 | 数据范围 10 | 1 ≤ L ≤ R ≤ 103 11 | 12 | 输出格式 13 | 输出最大的异或和 14 | 15 | 样例输入 16 | 1 17 | 10 18 | 19 | 样例输出 20 | 15 21 | 22 | 样例解释 23 | 当B = 10, A = 5时,异或和最大。 24 | 1010 xor 0101 = 1111,所以答案是 15. 25 | 26 | Sample Input 27 | 10 28 | 15 29 | Sample Output 30 | 7 31 | 32 | Explanation 33 | Here two pairs (10, 13) and (11, 12) have maximum xor value 7, and this is the answer. 34 | */ 35 | 36 | /** 37 | * Approach: Mathematics 38 | * To maximize A xor B, we want A and B to differ as much as possible at every bit index. 39 | * We first find the most significant bit that we can force to differ by looking at L and R. 40 | * For all of the lesser significant bits in A and B, we can always ensure that they differ and still have L <= A <= B <= R. 41 | * Our final answer will be the number represented by all 1s starting from the most significant bit that differs between A and B 42 | * Let's try an example 43 | * L = 10111 44 | * R = 11100 45 | * _X___ <-- that's most significant differing bit 46 | * 01111 <-- here's our final answer 47 | * 48 | * Notice how we don't directly calculate the values of A and B. 49 | */ 50 | 51 | import java.util.*; 52 | 53 | public class Solution { 54 | static int maximizingXor(int l, int r) { 55 | int xor = l ^ r; 56 | int significantBit = 31 - Integer.numberOfLeadingZeros(xor); 57 | int result = (1 << (significantBit + 1)) - 1; 58 | return result; 59 | } 60 | 61 | // with no additional method calls (numberOfLeadingZeros) 62 | static int maximizingXor2(int l, int r) { 63 | int xor = l ^ r; 64 | int result = 0; 65 | while (xor > 0) { 66 | result <<= 1; 67 | result |= 1; 68 | xor >>= 1; 69 | } 70 | return result; 71 | } 72 | 73 | public static void main(String[] args) { 74 | Scanner in = new Scanner(System.in); 75 | int l = in.nextInt(); 76 | int r = in.nextInt(); 77 | int result = maximizingXor(l, r); 78 | System.out.println(result); 79 | in.close(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Path in Matrix.java: -------------------------------------------------------------------------------- 1 | /* 2 | Given a N X N matrix Matrix[N][N] of positive integers. 3 | There are only three possible moves from a cell Matrix[r][c]. 4 | 1. Matrix[r+1][c] 5 | 2. Matrix[r+1][c+1] 6 | 3. Matrix[r+1][c-1] 7 | Starting from any column in row 0, return the largest sum of any of the paths up to row N-1. 8 | 9 | Input: 10 | The first line of the input contains an integer T denoting the number of test cases. 11 | The description of T test cases follows. 12 | The first line of each test case contains a single integer N denoting the order of matrix. 13 | Next line contains N*N integers denoting the elements of the matrix in row-major form. 14 | 15 | Output: 16 | Output the largest sum of any of the paths starting from any cell of row 0 to any cell of row N-1. 17 | Print the output of each test case in a new line. 18 | 19 | Constraints: 20 | 1<=T<=20 21 | 2<=N<=20 22 | 1<=Matrix[i][j]<=1000 (for all 1<=i<=N && 1<=j<=N) 23 | 24 | Example: 25 | Input: 26 | 1 27 | 2 28 | 348 391 618 193 29 | 30 | Output: 31 | 1009 32 | 33 | Explanation: In the sample test case, the path leading to maximum possible sum is 391->618. (391 + 618 = 1009) 34 | 35 | OJ地址:https://practice.geeksforgeeks.org/problems/path-in-matrix/0#ExpectOP 36 | */ 37 | 38 | /** 39 | * Approach: Matrix Dp 40 | * 矩阵 DP 的问题,与 基本相同。 41 | * dp[i][j] 仅仅由 上一行的 3个 元素值所决定。 42 | * 因此我们可以利用 滚动数组 对其 空间复杂度 进行优化。 43 | * 根据状态方程分析可得:我们只需要存储 两行的结果 即可。 44 | * 45 | * 时间复杂度:O(n^2); 空间复杂度:O(n) 46 | */ 47 | 48 | import java.util.*; 49 | import java.lang.*; 50 | import java.io.*; 51 | 52 | class GFG { 53 | public static void main (String[] args) { 54 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 55 | int T = sc.nextInt(); 56 | for (int i = 0; i < T; i++) { 57 | int n = sc.nextInt(); 58 | int[][] matrix = new int[n][n]; 59 | for (int row = 0; row < n; row++) { 60 | for (int col = 0; col < n; col++) { 61 | matrix[row][col] = sc.nextInt(); 62 | } 63 | } 64 | System.out.println(getResult(matrix)); 65 | } 66 | } 67 | 68 | private static int getResult(int[][] matrix) { 69 | if (matrix == null || matrix.length == 0) { 70 | return 0; 71 | } 72 | 73 | int n = matrix.length; 74 | // Using sliding array to optimize the extra space 75 | int[][] dp = new int[2][n]; 76 | // Initialize the first row 77 | for (int i = 0; i < n; i++) { 78 | dp[0][i] = matrix[0][i]; 79 | } 80 | for (int i = 1; i < n; i++) { 81 | for (int j = 0; j < n; j++) { 82 | // get the max value of three moving method and plus matrix[i][j] 83 | dp[i & 1][j] = Math.max(dp[(i-1) & 1][j], // the first move method 84 | Math.max(dp[(i-1) & 1][j + 1 >= n ? n - 1 : j + 1], // the second move method 85 | dp[(i-1) & 1][j - 1 < 0 ? 0 : j - 1])) + matrix[i][j]; // the third move method 86 | } 87 | } 88 | 89 | int max = Integer.MIN_VALUE; 90 | // Get the result 91 | for (int i : dp[(n - 1) & 1]) { 92 | max = Math.max(max, i); 93 | } 94 | return max; 95 | } 96 | } -------------------------------------------------------------------------------- /Random Pool(随机池).java: -------------------------------------------------------------------------------- 1 | /* 2 | 设计一种结构, 在该结构中有如下三个功能: 3 | insert(key):将某个key加入到该结构, 做到不重复加入。 4 | delete(key):将原本在结构中的某个key移除。 5 | getRandom():等概率随机返回结构中的任何一个key。 6 | 要求:Insert、delete和 getRandom 方法的时间复杂度都是 O(1) 7 | */ 8 | 9 | /** 10 | * Approach: Two HashMap 11 | * 起初考虑使用 数组 来实现这个结构,但是后来发现行不通。 12 | * 这是因为数组在实现 insert 和 getRandom 的时候确实没问题,但是无法处理 delete 的问题。 13 | * 如果我们需要删除一个特定的 Key,那么我们需要遍历一遍整个数组,这与要求的 O(1) 时间复杂度不符合。 14 | * 因此很自然地,插入,删除,查询 均为 O(1) 时间复杂的毫无疑问就是 HashMap 这个数据结构了。 15 | * 16 | * 但是使用 HashMap 的时候,我们也遇到了一个问题,如何保证在 O(1) 的时间内获得一个随机 Key (概率相等) 17 | * 我们通常会选择使用 Random 这个方法来实现,但是 Key 并不一定就是 int 类型,或者说大部分情况下并不是 int. 18 | * 所以我们还需要储存一个 key 所对应的 index 信息,以便进行随机获得 key. 19 | * 因此我们使用了两个 HashMap,分别储存 (Key, Index) 和 (Index, Key). 20 | * 这样我们便可以轻易地通过 key 或者 index 获得相对应的信息。 21 | * 22 | * 接下来我们还需要处理一个问题:delete操作 将会导致 [0...size-1] 范围上出现空当。 23 | * 这就会使得我们通过 random.nextInt(size) 获得的 index 很可能并没有对应的 Key 储存在上面。 24 | * 因此我们将 delete操作 改为如下操作: 25 | * 1. 首先获得需要删除的 Key 对应的 Index. 26 | * 2. 获取最后一个 lastIndex 以及对应的 lastKey. 27 | * 3. 修改两张 HashMap,将要删除的 deleteIndex 对应的 Key 改为 lastKey; 28 | * 同时将 lastKey 对应的 Index 改为 deleteIndex。最后 size--。 29 | * 即将 lastKey 与将被删除的 deleteKey 做了了交互(当然 index 也做了相应的修改) 30 | * 效果就相当于用lastKey去 填补 了因为 delete操作 导致的空当。 31 | */ 32 | import java.util.HashMap; 33 | 34 | public class RandomPool { 35 | 36 | public static class Pool { 37 | private HashMap keyIndexMap; 38 | private HashMap indexKeyMap; 39 | private int size; 40 | 41 | public Pool() { 42 | this.keyIndexMap = new HashMap<>(); 43 | this.indexKeyMap = new HashMap<>(); 44 | this.size = 0; 45 | } 46 | 47 | public void insert(String key) { 48 | if (!keyIndexMap.containsKey(key)) { 49 | keyIndexMap.put(key, size); 50 | indexKeyMap.put(size++, key); 51 | } 52 | } 53 | 54 | public void delete(String key) { 55 | if (keyIndexMap.containsKey(key)) { 56 | // 获取将被删除的 key 所对应的 deleteIndex 57 | int deleteIndex = keyIndexMap.get(key); 58 | // 获取map中的最后一个 index 59 | int lastIndex = --size; 60 | // 获取最后一个index所对应的 lastKey 61 | String lastKey = indexKeyMap.get(lastIndex); 62 | // 利用 lastKey 覆盖掉 deleteKey 的位置 63 | // 即将 lastKey 与 deleteKey 的位置信息互换(其实只是将 lastKey 的信息改了而已) 64 | keyIndexMap.put(lastKey, deleteIndex); 65 | indexKeyMap.put(deleteIndex, lastKey); 66 | // 最后在两个 map 中移除所要删除的信息 67 | keyIndexMap.remove(key); 68 | indexKeyMap.remove(lastIndex); 69 | } 70 | } 71 | 72 | public String getRandom() { 73 | if (size == 0) { 74 | return null; 75 | } 76 | int randomIndex = (int) (Math.random() * size); // 0 ~ size -1 77 | return indexKeyMap.get(randomIndex); 78 | } 79 | 80 | } 81 | 82 | public static void main(String[] args) { 83 | Pool pool = new Pool(); 84 | pool.insert("boku"); 85 | pool.insert("kimi"); 86 | pool.insert("sekai"); 87 | System.out.println(pool.getRandom()); 88 | System.out.println(pool.getRandom()); 89 | System.out.println(pool.getRandom()); 90 | System.out.println(pool.getRandom()); 91 | System.out.println(pool.getRandom()); 92 | System.out.println(pool.getRandom()); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Reverse a stack using recursion.java: -------------------------------------------------------------------------------- 1 | /* 2 | Description: 3 | Write a program to reverse a stack using recursion. 4 | You are not allowed to use loop constructs like while, for..etc, 5 | and you can only use the following ADT functions on Stack S: 6 | isEmpty(S) 7 | push(S) 8 | pop(S) 9 | 10 | This is a quetions on: http://www.geeksforgeeks.org/reverse-a-stack-using-recursion/ 11 | */ 12 | 13 | /** 14 | * Approach 1: Using Recursion 15 | * 不适用其他的数据结构或者开辟额外的空间,只使用 递归 来完成栈的逆序操作。 16 | * (这里递归过程所使用的栈空间不计算为额外空间) 17 | * 这道题目的想法还是很简单的,就是将 Stack 中所有的元素 pop() 出来,直到 Stack 为空。 18 | * 然后再将 pop() 出来的元素依次 push() 回去就能够完成逆序了。 19 | * 但是本题的难点在于要求使用 递归 来实现这个过程,这就意味着我们不能够使用额外的空间来存储被 pop() 出来的元素。 20 | * 21 | * 为了实现这一点,本题使用了两个 递归函数。(本题这两个递归函数的应用还是十分令人佩服的) 22 | * getAndRemoveLastElement(): 该函数的功能是获得并移除当前栈的栈底元素 23 | * 为了更好地理解该函数,我们以 1, 2, 3 为例(栈顶到栈底顺序) 24 | * 第一次调用:栈变成 2, 3; element = 1, stack非空, last = ? (进行递归调用) 25 | * 第二次调用:栈变成 3; element = 2, stack非空, last = ? (进行递归调用) 26 | * 第三次调用:栈变成 null; element = 3, stack为空, 返回 element = 3 27 | * 回到第二次调用处:element = 2, last = 3, 将 element 压入栈中; 栈变成 2 28 | * 回到第一次调用处:element = 1, last = 3, 将 element 压入栈中; 栈变成 1, 2 29 | * 执行完毕,返回 3,栈变成 1, 2 30 | * reverse(): 该函数的功能是利用 getAndRemoveLastElement() 函数从而实现对栈的逆序 31 | * 这里同样以 1, 2, 3 为例。 32 | * 第一次调用:stack非空; 调用 getAndRemoveLastElement() 函数,获得 last = 3; 栈变成 1, 2. 继续递归调用 33 | * 第二次调用:stack非空; 调用 getAndRemoveLastElement() 函数,获得 last = 2; 栈变成 1. 继续递归调用 34 | * 第三次调用:stack非空; 调用 getAndRemoveLastElement() 函数,获得 last = 1; 栈变成 空. 继续递归调用 35 | * 第四次调用:stack为空,返回结束。 36 | * 回到第三次调用处:将 last = 1 push回栈中; 栈变成 1 37 | * 回到第二次调用处:将 last = 2 push回栈中; 栈变成 2, 1 38 | * 回到第一次调用处:将 last = 3 push回栈中; 栈变成 3, 2, 1 39 | * 整个过程执行完毕,栈变成了 3, 2, 1 完成逆序。 40 | * 至此,可见本题对于递归的使用可以说是相当好了... 41 | */ 42 | import java.util.Stack; 43 | 44 | public class Main { 45 | // Using Stack class for stack implementation 46 | static Stack stack = new Stack<>(); 47 | 48 | // Below is a recursive function that 49 | // get and remove the element at the bottom of a stack 50 | static char getAndRemoveLastElement() { 51 | char element = stack.pop(); 52 | if (stack.isEmpty()) { 53 | return element; 54 | } else { 55 | char last = getAndRemoveLastElement(); 56 | stack.push(element); 57 | return last; 58 | } 59 | } 60 | 61 | // Below is the function that reverses the given stack using getAndRemoveLastElement() 62 | static void reverse() { 63 | if (stack.isEmpty()) { 64 | return; 65 | } 66 | char last = getAndRemoveLastElement(); 67 | reverse(); 68 | stack.push(last); 69 | } 70 | 71 | public static void main(String[] args) { 72 | //push elements into the stack 73 | stack.push('1'); 74 | stack.push('2'); 75 | stack.push('3'); 76 | stack.push('4'); 77 | System.out.println("Original Stack"); 78 | System.out.println(stack); 79 | 80 | //function to reverse the stack 81 | reverse(); 82 | System.out.println("Reversed Stack"); 83 | System.out.println(stack); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Swap Two Integers.java: -------------------------------------------------------------------------------- 1 | import java.lang.reflect.Field; 2 | 3 | /** 4 | * @author Administrator 5 | * 面试题目: 6 | * 主要定义了两个 Integer 变量,通过调用 swap 方法后, 7 | * 使得这两个变量交换数据,然后控制台打印交换前后的吧值。 8 | * 请写出 swap 方法的实现 9 | */ 10 | 11 | public class Solution { 12 | public static void main(String[] args) { 13 | Integer a = 1; 14 | Integer b = 2; 15 | System.out.println("before swap: a=" + a + ", b=" + b); 16 | swap(a, b); 17 | System.out.println("after swap: a=" + a + ", b=" + b); 18 | } 19 | 20 | /** 21 | * 解法: 22 | * 因为 java 无法像 C/C++ 一样通过指针来实现两个数的交换 23 | * 故我们需要使用 java 的反射机制来解决问题。 24 | * 记得需要 catch exception 25 | * @param num1 26 | * @param num2 27 | */ 28 | private static void swap(Integer num1, Integer num2) { 29 | try { 30 | Field field = Integer.class.getDeclaredField("value"); 31 | field.setAccessible(true); 32 | int temp = num1.intValue(); 33 | field.set(num1, num2); 34 | field.set(num2, new Integer(temp)); 35 | } catch (Exception ex) { 36 | ex.printStackTrace(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /Tencent_微信红包.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/Tencent_微信红包.java -------------------------------------------------------------------------------- /Tencent_游戏任务标记.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/Tencent_游戏任务标记.java -------------------------------------------------------------------------------- /Tencent_素数对.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/Tencent_素数对.java -------------------------------------------------------------------------------- /n个数里最小的k个.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 找出n个数里最小的k个 4 | 5 | 输入描述: 6 | 每个测试输入包含空格分割的n+1个整数,最后一个整数为k值,n 7 | 不超过100。 8 | 输出描述: 9 | 输出n个整数里最小的k个数。升序输出 10 | 11 | 示例1 12 | 输入 13 | 3 9 6 8 -10 7 -11 19 30 12 23 5 14 | 输出 15 | -11 -10 3 6 7 16 | */ 17 | 18 | /** 19 | * Approach: QuickSelect | Partition 20 | * 经典的 TopK 问题,使用 快排Partition 的思想即可解决。 21 | * 22 | * 类似的问题有: 23 | * https://github.com/cherryljr/LintCode/blob/master/Kth%20Largest%20Element.java 24 | * 对于 Partition 过程不了解的可以参考: 25 | * https://github.com/cherryljr/LintCode/blob/master/Sort%20Colors.java 26 | */ 27 | 28 | import java.io.BufferedReader; 29 | import java.io.IOException; 30 | import java.io.InputStreamReader; 31 | import java.util.Arrays; 32 | 33 | public class Main { 34 | public static void main(String[] args) throws IOException { 35 | BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); 36 | String[] strings = bf.readLine().split(" "); 37 | int[] nums = new int[strings.length - 1]; 38 | for (int i = 0; i < nums.length; i++) { 39 | nums[i] = Integer.valueOf(strings[i]); 40 | } 41 | int k = Integer.valueOf(strings[strings.length - 1]); 42 | 43 | helper(nums, 0, nums.length - 1, k); 44 | int[] rst = new int[k]; 45 | for (int i = 0; i < k; i++) { 46 | rst[i] = nums[i]; 47 | } 48 | Arrays.sort(rst); 49 | for (int i = 0; i < k - 1; i++) { 50 | System.out.print(rst[i] + " "); 51 | } 52 | System.out.println(rst[k - 1]); 53 | } 54 | 55 | private static void helper(int[] nums, int left, int right, int k) { 56 | if (left == right) { 57 | return; 58 | } 59 | 60 | int position = partition(nums, left, right); 61 | if (position == k) { 62 | return; 63 | } else if (position > k){ 64 | helper(nums, left, position - 1, k); 65 | } else { 66 | helper(nums, position + 1, right, k); 67 | } 68 | } 69 | 70 | private static int partition(int[] nums, int left, int right) { 71 | int less = left - 1, more = right; 72 | while (left < more) { 73 | if (nums[left] < nums[right]) { 74 | swap(nums, ++less, left++); 75 | } else if (nums[left] > nums[right]) { 76 | swap(nums, --more, left); 77 | } else { 78 | left++; 79 | } 80 | } 81 | swap(nums, more,right); 82 | return less + 1; 83 | } 84 | 85 | private static void swap(int[] nums, int i, int j) { 86 | int temp = nums[i]; 87 | nums[i] = nums[j]; 88 | nums[j] = temp; 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /一笔画.java: -------------------------------------------------------------------------------- 1 | /* 2 | 咱们来玩一笔画游戏吧,规则是这样的:有一个连通的图,能否找到一个恰好包含了所有的边,并且没有重复的路径。 3 | 4 | 输入描述: 5 | 输入包含多组数据。 6 | 每组数据的第一行包含两个整数n和m (2≤n, m≤1000),其中n是顶点的个数,m是边的条数。 7 | 紧接着有m行,每行包含两个整数from和to (1 ≤ from, to ≤ n, from != to),分别代表边的两端顶点。 8 | 边是双向的,并且两个顶点之间可能不止一条边。 9 | 10 | 输出描述: 11 | 对应每一组输入,如果能一笔画则输出“Yes”;否则输出“No”。 12 | 13 | 示例1 14 | 输入 15 | 3 3 16 | 1 2 17 | 2 3 18 | 1 3 19 | 4 7 20 | 1 2 21 | 2 1 22 | 1 3 23 | 1 4 24 | 1 4 25 | 2 3 26 | 4 3 27 | 输出 28 | Yes 29 | No 30 | */ 31 | 32 | /** 33 | * Approach: 欧拉回路问题 34 | * 欧拉回路问题(七桥问题),如果一个联通图,奇数点的个数为0或者2的时候,那么一定存在一个欧拉回路。 35 | * 小学奥数题...应该都见过没啥好说的了... 36 | * 37 | * 对此想要有更进一步了解的可以参考: 38 | * https://blog.csdn.net/u011466175/article/details/18825453 39 | */ 40 | 41 | import java.util.Scanner; 42 | 43 | public class Main { 44 | public static void main(String[] args) { 45 | Scanner sc = new Scanner(System.in); 46 | while (sc.hasNext()) { 47 | int n = sc.nextInt(), m = sc.nextInt(); 48 | int[] points = new int[n]; 49 | for (int i = 0; i < m; i++) { 50 | points[sc.nextInt() - 1]++; 51 | points[sc.nextInt() - 1]++; 52 | } 53 | 54 | // Check the edges of each point 55 | int count = 0; 56 | for (int i = 0; i < n; i++) { 57 | if ((points[i] & 1) == 1) { 58 | count++; 59 | } 60 | } 61 | System.out.println((count == 0 || count == 2) ? "Yes" : "No"); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /两个有序数组相加和的TopK.java: -------------------------------------------------------------------------------- 1 | /* 2 | 【题目】 3 | 给定两个数组arr1和arr2,arr1和arr2按照从小到大的顺序排列,再给定一个整数k, 4 | 返回来自arr1和arr2的两个数相加和最大的前k个,两个数必须分别来自两个数组。 5 | 【举例】 6 | arr1=[1,2,3,4,5], arr2=[3,5,7,9,11], k=4。 7 | 返回数组[16,15,14,14]。 8 | 【要求】 9 | 时间复杂度达到O(klogk)。 10 | */ 11 | 12 | /** 13 | * Approach: PriorityQueue 14 | * topK 问题最经常使用到的就是 堆 这个数据结构了。 15 | * 因为题目要求 O(klogk) 的时间复杂度,这就意味着我们只能开 O(k) 大小的堆。 16 | * 给定的两个数组是有序的,因此最大值是可以确定的。 17 | * 那么首先我们将最大值加入到 最大堆 中。 18 | * 而次大值只会来自于 arr1 中的最大值 与 arr2 中的次大值之和 或者是 arr1 中的次大值 与 arr2 中的最大值之和 19 | * 对应于 arr1 与 arr2 各个位置上对应的两两之和的矩阵,就是 最大值 的 左侧 和 上面 的两个值。 20 | * 因此我们每次的操作就是: 21 | * 从 maxHeap 中 poll 出来当前最大值,并将其相邻的两个备选值加入到 maxHeap 中。 22 | * 同时了为了保证矩阵中同一个位置上的数不会被重复添加,我们需要一个 Set 来记录当前 sum 所对应的 row 和 column. 23 | * 如: 24 | * X 7 25 | * 6 8 26 | * 当将 6,7 poll出去之和,我们会重复添加 X 位置的值,这是错误的。 27 | */ 28 | 29 | import java.io.BufferedInputStream; 30 | import java.util.*; 31 | 32 | public class Main { 33 | public static void main(String[] args) { 34 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 35 | int n = sc.nextInt(), m = sc.nextInt(), k = sc.nextInt(); 36 | int[] arr1 = new int[n]; 37 | int[] arr2 = new int[m]; 38 | for (int i = 0; i < n; i++) { 39 | arr1[i] = sc.nextInt(); 40 | } 41 | for (int i = 0; i < m; i++) { 42 | arr2[i] = sc.nextInt(); 43 | } 44 | 45 | int[] rst = topKSum(arr1, arr2, k); 46 | for (int num : rst) { 47 | System.out.print(num + " "); 48 | } 49 | } 50 | 51 | private static int[] topKSum(int[] arr1, int[] arr2, int topK) { 52 | if (arr1 == null || arr1.length == 0 || arr2 == null || arr2.length == 0 || topK <= 0) { 53 | return new int[]{}; 54 | } 55 | 56 | int n = arr1.length, m = arr2.length; 57 | // 看 topK 大小是否已经超过了两个数列所有的两两之和(矩阵大小) 58 | topK = Math.min(topK, n * m); 59 | int rstIndex = 0; 60 | int[] rst = new int[topK]; 61 | PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b.sum - a.sum); 62 | // 用于记录某个位置的值是否已经被添加过了 63 | Set visited = new HashSet<>(); 64 | maxHeap.offer(new Pair(n -1 , m - 1, arr1[n - 1] + arr2[m - 1])); 65 | visited.add((n - 1) + "#" + (m - 1)); 66 | 67 | while (rstIndex < topK) { 68 | Pair curr = maxHeap.poll(); 69 | rst[rstIndex++] = curr.sum; 70 | int rowIndex = curr.row, colIndex = curr.col; 71 | // 如果存在且未被添加过,则将 curr 上面的点放入 maxHeap 中 72 | if (rowIndex > 0 && !visited.contains((rowIndex - 1) + "#" + colIndex)) { 73 | maxHeap.offer(new Pair(rowIndex - 1, colIndex, arr1[rowIndex - 1] + arr2[colIndex])); 74 | visited.add((rowIndex - 1) + "#" + colIndex); 75 | } 76 | // 如果存在且未被添加过,则将 curr 左侧的点放入 maxHeap 中 77 | if (colIndex > 0 && !visited.contains(rowIndex + "#" + (colIndex - 1))) { 78 | maxHeap.offer(new Pair(rowIndex, colIndex - 1, arr1[rowIndex] + arr2[colIndex - 1])); 79 | visited.add(rowIndex + "#" + (colIndex - 1)); 80 | } 81 | } 82 | 83 | return rst; 84 | } 85 | 86 | static class Pair { 87 | int row, col; 88 | int sum; 89 | 90 | public Pair(int row, int col, int sum) { 91 | this.row = row; 92 | this.col = col; 93 | this.sum = sum; 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /二分法求局部最小值.java: -------------------------------------------------------------------------------- 1 | 与 LeetCode 中的 Find Peak Element 解法相同。 2 | 该题只是将 局部最大值 换成了求 局部最小值。 3 | Find Peak Element: https://leetcode.com/problems/find-peak-element/discuss/ 4 | 5 | 该道题目给我们最大的启发就是: 6 | 不一定要有序才能进行二分操作。 7 | 只要是存在排他性,它就能够进行二分操作。 8 | 1. 一边肯定有,另一边可能有; 9 | 2. 一边可能有,另一边绝对没有。 10 | 11 | /* 12 | 给定一个无序数组 nums[],相邻数字不同。返回任意一个局部最小值。 13 | 局部最小值的定义为: 14 | 1. 对于 第一个元素 而言,只要它比 第二个元素 要小,则就是局部最小值。 15 | 同理,对于 最后一个元素 而言,只要它比 倒数第二个元素 要小,则就是局部最小值。 16 | 2. 对于数据中间的元素,如果一个数相邻的两个数都比他大,那么他就是局部最小值。 17 | */ 18 | 19 | import java.util.*; 20 | 21 | public class Main { 22 | public static void main(String[] args) { 23 | int[] A = {19, 11, 3, 8, 10}; 24 | System.out.println(BinarySearch(A)); 25 | } 26 | 27 | private static int BinarySearch(int[] nums) { 28 | // 规定当数组中只有一个数时,不存在局部最小值 29 | if (nums == null || nums.length <= 1) { 30 | return 0; 31 | } 32 | 33 | int len = nums.length; 34 | if (nums[0] < nums[1]) { 35 | return nums[0]; 36 | } 37 | if (nums[len - 1] < nums[len - 2]) { 38 | return nums[len - 1]; 39 | } 40 | 41 | int start = 0; 42 | int end = len - 1; 43 | while (start + 1 < end) { 44 | int mid = start + (end - start) / 2; 45 | if (nums[mid] > nums[mid + 1]) { 46 | start = mid + 1; 47 | } else { 48 | end = mid; 49 | } 50 | } 51 | 52 | return (nums[start] < nums[start + 1]) ? nums[start] : nums[end]; 53 | } 54 | } -------------------------------------------------------------------------------- /京东_保卫方案.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/京东_保卫方案.java -------------------------------------------------------------------------------- /京东_幸运数.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/京东_幸运数.java -------------------------------------------------------------------------------- /京东_神奇数.java: -------------------------------------------------------------------------------- 1 | 2017 年京东研发类编程考题答案。 2 | 思路: 3 | 第一步就是要把数字 n 每一位上的数拆分出来以方便我们的计算。 4 | 第二步,分成两个部分使得和相等这就意味着, 5 | 我们需要拆分得到的数组 int[] num中找到两个 Target.使得 Target1 = Target2. 6 | 第三步,说到这里 背包问题 的实质已经暴露出来了。因此我们使用 DP 来解决该问题。 7 | 而问题的实质就是:在一个数组能否中找到一个和为 sum / 2 的 subarray. 8 | 类似的问题可以常见:LintCode 的 Backpack VI 问题 9 | 解答也可以在我的 github 中找到: https://github.com/cherryljr/LintCode/blob/master/Backpack%20VI.java 10 | 11 | /* 12 | 题目描述:神奇数(仅凭记忆描述,原题放出后会补上) 13 | 神奇数的定义是,将一个数的各个位上的数取出来。分成两个部分。 14 | 当这两个部分的书之和相等时,则意味着该数是一个神奇数。 15 | 比如:123,可以分成 {1, 2} 和 {3}. 1+2=3.故 123 是神奇数 16 | 要求: 17 | 输入两个数 l 和 r。要求能够找出区间 [l, r] 中所有的神奇数的输出。 18 | 19 | 测试案例: 20 | 1 50 21 | 输出解果: 22 | 4 23 | */ 24 | 25 | import java.util.*; 26 | import java.io.*; 27 | 28 | public class LintCode { 29 | public static void main(String[] args) { 30 | Scanner sc = new Scanner(System.in); 31 | int l = sc.nextInt(); // 输入 l 32 | int r = sc.nextInt(); // 输入 r 33 | int count = 0; // 计算有几个数 34 | 35 | // 遍历 l ~ r 闭区间 36 | for (int i = l; i <= r; i++) { 37 | // 将数字 i 转换为 int[] 38 | String s = String.valueOf(i); 39 | char[] ch = s.toCharArray(); 40 | int[] nums = new int[ch.length]; 41 | for (int j = 0; j < nums.length; j++) { 42 | nums[j] = ch[j] - '0'; 43 | } 44 | // 判断是否为神奇数字 45 | if (isMagicalNumber(nums)) { 46 | count++; 47 | } 48 | } 49 | System.out.println(count); 50 | } 51 | 52 | // 判断函数 53 | public static boolean isMagicalNumber(int[] nums) { 54 | int len = nums.length; 55 | int sum = 0; 56 | for (int i = 0; i < len; i++) { 57 | sum += nums[i]; 58 | } 59 | if (sum % 2 == 1) { 60 | return false; 61 | } 62 | sum /= 2; 63 | 64 | // State & Initialize 65 | boolean[] dp = new boolean[sum + 1]; 66 | for (int i = 0; i <= sum; i++) { 67 | dp[i] = false; 68 | } 69 | dp[0] = true; 70 | 71 | // Function 72 | for (int i = 0; i < len; i++) { 73 | for (int j = sum; j >= nums[i]; j--) { 74 | dp[j] |= dp[j - nums[i]]; 75 | } 76 | } 77 | 78 | // Answer 79 | return dp[sum]; 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /京东_进制均值.java: -------------------------------------------------------------------------------- 1 | 该题考察点相对清晰。 2 | 1. 如何计算一个数的 k 进制 3 | 2. 求两个数的最大公约数 GCD 4 | 5 | 做法: 6 | 通过方法 averageA 可以求出一个数 radix 进制的每一位数上的和, 7 | 然后通过 calculate 方法求得将 2~A-1 进制表达的各个位数之和全部加起来的值 8 | 最后求出 sum 和 A-2 的最大公约数,除以 GCD,从而得到最后结果。 9 | 10 | /* 11 | 尽管是一个CS专业的学生,小B的数学基础很好并对数值计算有着特别的兴趣,喜欢用计算机程序来解决数学问题, 12 | 现在,她正在玩一个数值变换的游戏。她发现计算机中经常用不同的进制表示一个数, 13 | 如十进制数123表达为16进制时只包含两位数7、11(B),用八进制表示为三位数1、7、3, 14 | 按不同进制表达时,各个位数的和也不同,如上述例子中十六进制和八进制中各位数的和分别是18和11,。 15 | 小B感兴趣的是,一个数A如果按2到A-1进制表达时,各个位数之和的均值是多少?她希望你能帮她解决这个问题? 16 | 所有的计算均基于十进制进行,结果也用十进制表示为不可约简的分数形式。 17 | 18 | 输入描述: 19 | 输入中有多组测试数据,每组测试数据为一个整数A(1 ≤ A ≤ 5000). 20 | 21 | 输出描述: 22 | 对每组测试数据,在单独的行中以X/Y的形式输出结果。 23 | 24 | 输入例子1: 25 | 5 26 | 3 27 | 28 | 输出例子1: 29 | 7/3 30 | 2/1 31 | */ 32 | 33 | import java.util.Scanner; 34 | 35 | public class Main { 36 | public static void main(String[] args) { 37 | Scanner sc = new Scanner(System.in); 38 | while (sc.hasNext()) { 39 | int num = sc.nextInt(); 40 | int sum = calculate(num); 41 | // 分子分母同时除以最大公约数, 化成最简形式 42 | System.out.printf("%d/%d\n", sum / GCD(sum, num-2), (num-2) / GCD(sum, num-2)); 43 | } 44 | 45 | } 46 | // 求解两个数的最大公约数 47 | public static int GCD(int a, int b) { 48 | int temp = 0; 49 | if (a < b) { 50 | temp = a; 51 | a = b; 52 | b = temp; 53 | } 54 | int c = a % b; 55 | if (c == 0) { 56 | return b; 57 | } else { 58 | return GCD(b, c); 59 | } 60 | } 61 | 62 | // 对每个数的 2 到 n-1 进制的位数进行求和 63 | public static int calculate(int n) { 64 | int radix = n - 1; 65 | int sum = 0; 66 | while (radix > 1) { 67 | sum += averageA(radix, n ); 68 | radix--; 69 | } 70 | 71 | return sum; 72 | } 73 | 74 | //对一个数的某一进制的数字进行求和 75 | public static int averageA(int radix, int n) { 76 | int sumOfN = 0; 77 | while (n > 0) { 78 | sumOfN += (n % radix); 79 | n /= radix; 80 | } 81 | return sumOfN; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /今日头条_手串.java: -------------------------------------------------------------------------------- 1 | /* 2 | 作为一个手串艺人,有金主向你订购了一条包含n个杂色串珠的手串——每个串珠要么无色,要么涂了若干种颜色。 3 | 为了使手串的色彩看起来不那么单调,金主要求,手串上的任意一种颜色(不包含无色),在任意连续的m个串珠里至多出现一次(注意这里手串是一个环形)。 4 | 手串上的颜色一共有c种。现在按顺时针序告诉你n个串珠的手串上,每个串珠用所包含的颜色分别有哪些。 5 | 请你判断该手串上有多少种颜色不符合要求。即询问有多少种颜色在任意连续m个串珠中出现了至少两次。 6 | 7 | 输入描述: 8 | 第一行输入n,m,c三个数,用空格隔开。(1 <= n <= 10000, 1 <= m <= 1000, 1 <= c <= 50) 9 | 接下来n行每行的第一个数num_i(0 <= num_i <= c)表示第i颗珠子有多少种颜色。接下来依次读入num_i个数字, 10 | 每个数字x表示第i颗柱子上包含第x种颜色(1 <= x <= c) 11 | 12 | 输出描述: 13 | 一个非负整数,表示该手链上有多少种颜色不符需求。 14 | 15 | 输入例子1: 16 | 5 2 3 17 | 3 1 2 3 18 | 0 19 | 2 2 3 20 | 1 2 21 | 1 3 22 | 23 | 输出例子1: 24 | 2 25 | 26 | 例子说明1: 27 | 第一种颜色出现在第1颗串珠,与规则无冲突。 28 | 第二种颜色分别出现在第 1,3,4颗串珠,第3颗与第4颗串珠相邻,所以不合要求。 29 | 第三种颜色分别出现在第1,3,5颗串珠,第5颗串珠的下一个是第1颗,所以不合要求。 30 | 总计有2种颜色的分布是有问题的。 31 | 这里第2颗串珠是透明的。 32 | */ 33 | 34 | /** 35 | * Approach: Sliding Window 36 | * 很显然考察滑动窗口的问题。使用 Map 即可解决。 37 | * 对于环形结构,我们可以采用多走 m-1 步的做法去解决。 38 | */ 39 | 40 | import java.util.*; 41 | 42 | public class Main { 43 | public static void main(String[] args) { 44 | Scanner sc = new Scanner(System.in); 45 | while (sc.hasNext()) { 46 | int n = sc.nextInt(), m = sc.nextInt(), c = sc.nextInt(); 47 | Bead[] beads = new Bead[n]; 48 | for (int i = 0; i < n; i++) { 49 | int count = sc.nextInt(); 50 | beads[i] = new Bead(); 51 | for (int j = 0; j < count; j++) { 52 | beads[i].color.add(sc.nextInt()); 53 | } 54 | } 55 | 56 | Map map = new HashMap<>(); 57 | Set record = new HashSet<>(); 58 | for (int i = 0; i < m; i++) { 59 | for (int color : beads[i].color) { 60 | if (map.containsKey(color)) { 61 | map.put(color, map.get(color) + 1); 62 | record.add(color); 63 | } else { 64 | map.put(color, 1); 65 | } 66 | } 67 | } 68 | 69 | // 环形结构 70 | for (int i = m; i < n + m; i++) { 71 | for (int color : beads[(i - m) % n].color) { 72 | int count = map.get(color); 73 | if (--count == 0) { 74 | map.remove(color); 75 | } else { 76 | map.put(color, count); 77 | } 78 | } 79 | 80 | for (int color : beads[i % n].color) { 81 | if (map.containsKey(color)) { 82 | map.put(color, map.get(color) + 1); 83 | record.add(color); 84 | } else { 85 | map.put(color, 1); 86 | } 87 | } 88 | } 89 | System.out.println(record.size()); 90 | } 91 | } 92 | 93 | static class Bead { 94 | List color; 95 | 96 | Bead() { 97 | this.color = new ArrayList<>(); 98 | } 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /今日头条_用户喜好.java: -------------------------------------------------------------------------------- 1 | /* 2 | 为了不断优化推荐效果,今日头条每天要存储和处理海量数据。 3 | 假设有这样一种场景:我们对用户按照它们的注册时间先后来标号,对于一类文章,每个用户都有不同的喜好值, 4 | 我们会想知道某一段时间内注册的用户(标号相连的一批用户)中,有多少用户对这类文章喜好值为k。 5 | 因为一些特殊的原因,不会出现一个查询的用户区间完全覆盖另一个查询的用户区间(不存在L1<=L2<=R2<=R1)。 6 | 7 | 输入描述: 8 | 输入: 第1行为n代表用户的个数 第2行为n个整数,第i个代表用户标号为i的用户对某类文章的喜好度 第3行为一个正整数q代表查询的组数 9 | 第4行到第(3+q)行,每行包含3个整数l,r,k代表一组查询,即标号为l<=i<=r的用户中对这类文章喜好值为k的用户的个数。 10 | 数据范围n <= 300000,q<=300000 k是整型 11 | 12 | 输出描述: 13 | 输出:一共q行,每行一个整数代表喜好值为k的用户的个数 14 | 15 | 输入例子1: 16 | 5 17 | 1 2 3 3 5 18 | 3 19 | 1 2 1 20 | 2 4 5 21 | 3 5 3 22 | 23 | 输出例子1: 24 | 1 25 | 0 26 | 2 27 | 28 | 例子说明1: 29 | 样例解释: 30 | 有5个用户,喜好值为分别为1、2、3、3、5, 31 | 第一组询问对于标号[1,2]的用户喜好值为1的用户的个数是1 32 | 第二组询问对于标号[2,4]的用户喜好值为5的用户的个数是0 33 | 第三组询问对于标号[3,5]的用户喜好值为3的用户的个数是2 34 | */ 35 | 36 | /** 37 | * Approach: HashMap + BinarySearch 38 | * 根据题目的数据量,可以推测出来该题的时间复杂度在 O(nlogn) 级别。 39 | * 所以很自然就能够想到 二分查找 的方法。 40 | * 首先利用 Map 统计每个喜好程序下的人员编号。 41 | * 因为按编号大小输入,所以map下的每个 list 里面的编号已经是有序的了(从小到大)。 42 | * 然后根据 left 和 right 在对应的 list 中进行查询人数区间。 43 | * 用到了 lowerBound 和 upperBound。 44 | * 注意处理一下查询目标不存在和查询范围越界的情况即可。 45 | */ 46 | 47 | import java.util.*; 48 | 49 | public class Main { 50 | public static void main(String[] args) { 51 | Scanner sc = new Scanner(System.in); 52 | while (sc.hasNext()) { 53 | int n = sc.nextInt(); // 用户个数 54 | Map> map = new HashMap<>(); 55 | for (int i = 0; i < n; i++) { 56 | map.computeIfAbsent(sc.nextInt(), x -> new ArrayList<>()).add(i); 57 | } 58 | 59 | int q = sc.nextInt(); // 查询组数 60 | for (int i = 0; i < q; i++) { 61 | int left = sc.nextInt() - 1, right = sc.nextInt() - 1, k = sc.nextInt(); 62 | if (!map.containsKey(k)) { 63 | System.out.println(0); 64 | } else { 65 | List list = map.get(k); 66 | int leftRst = lowerBound(list, left); 67 | int rightRst = upperBound(list, right); 68 | // list 中不存在对应的范围(查询范围越界) 69 | // left > list中的最大值 || right < list中的最小值 70 | if (leftRst == list.size() || right == -1) { 71 | System.out.println(0); 72 | continue; 73 | } 74 | System.out.println(rightRst - leftRst + 1); 75 | } 76 | } 77 | } 78 | sc.close(); 79 | } 80 | 81 | private static int lowerBound(List list, int target) { 82 | int left = 0, right = list.size(); 83 | while (left < right) { 84 | int mid = left + (right - left >> 1); 85 | if (target <= list.get(mid)) { 86 | right = mid; 87 | } else { 88 | left = mid + 1; 89 | } 90 | } 91 | return left; 92 | } 93 | 94 | private static int upperBound(List list, int target) { 95 | int left = -1, right = list.size() - 1; 96 | while (left < right) { 97 | int mid = left + (right - left + 1 >> 1); 98 | if (target >= list.get(mid)) { 99 | left = mid; 100 | } else { 101 | right = mid - 1; 102 | } 103 | } 104 | return right; 105 | } 106 | 107 | } 108 | 109 | -------------------------------------------------------------------------------- /修补木桶.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目: 修补木桶 3 | 时间限制:10000ms 4 | 单点时限:1000ms 5 | 内存限制:256MB 6 | 7 | 描述 8 | 一只木桶能盛多少水,并不取决于桶壁上最高的那块木板,而恰恰取决于桶壁上最短的那块。 9 | 已知一个木桶的桶壁由N块木板组成,第i块木板的长度为Ai。 10 | 现在小Hi有一个快捷修补工具,每次可以使用修补工具将连续的不超过L块木板提高至任意高度。 11 | 已知修补工具一共可以使用M次(M*L> 1); 73 | if (minFixTimes(n, l, mid, bricks) <= m) { 74 | // 当 最少修补次数 <= m 时,说明 最短木块的修补高度还能继续增加 75 | left = mid; 76 | } else { 77 | right = mid - 1; 78 | } 79 | } 80 | 81 | System.out.println(left); 82 | } 83 | 84 | private static int minFixTimes(int n, int l, int mid, int[] bricks) { 85 | int minTime = Integer.MAX_VALUE; 86 | for (int i = 0; i < n; i++) { // 枚举起点位置 87 | int num = 0; 88 | for (int j = 0; j < n; j++) { // 枚举距离起点位置的距离 89 | int k = (i + j) % n; 90 | if (bricks[k] < mid) { // 小于 mid 则说明需要修补 91 | num++; 92 | j += (l - 1); 93 | } 94 | } 95 | minTime = Math.min(minTime, num); 96 | } 97 | return minTime; 98 | } 99 | } 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /分数调查.java: -------------------------------------------------------------------------------- 1 | /* 2 | #1515 : 分数调查 3 | 时间限制:10000ms 4 | 单点时限:1000ms 5 | 内存限制:256MB 6 | 7 | 描述 8 | 小Hi的学校总共有N名学生,编号1-N。学校刚刚进行了一场全校的古诗文水平测验。 9 | 学校没有公布测验的成绩,所以小Hi只能得到一些小道消息,例如X号同学的分数比Y号同学的分数高S分。 10 | 小Hi想知道利用这些消息,能不能判断出某两位同学之间的分数高低? 11 | 12 | 输入 13 | 第一行包含三个整数N, M和Q。N表示学生总数,M表示小Hi知道消息的总数,Q表示小Hi想询问的数量。 14 | 以下M行每行三个整数,X, Y和S。表示X号同学的分数比Y号同学的分数高S分。 15 | 以下Q行每行两个整数,X和Y。表示小Hi想知道X号同学的分数比Y号同学的分数高几分。 16 | 对于50%的数据,1 <= N, M, Q <= 1000 17 | 对于100%的数据,1 <= N, M, Q<= 100000 1 <= X, Y <= N -1000 <= S <= 1000 18 | 数据保证没有矛盾。 19 | 20 | 输出 21 | 对于每个询问,如果不能判断出X比Y高几分输出-1。否则输出X比Y高的分数。 22 | 23 | 样例输入 24 | 10 5 3 25 | 1 2 10 26 | 2 3 10 27 | 4 5 -10 28 | 5 6 -10 29 | 2 5 10 30 | 1 10 31 | 1 5 32 | 3 5 33 | 34 | 样例输出 35 | -1 36 | 20 37 | 0 38 | */ 39 | 40 | /** 41 | * Approach: Union Find 42 | * 一旦两个学生通过 消息M 建立起连接,我们就可以通过这个消息来判断二者的分差。 43 | * 并且该信息是具有 传递性 的,即得知了 A,B 和 B,C 的关系后,我们可以推出 A,C 的关系。 44 | * 即相当于 A,B,C 形成一个连通区,我们可以得知连通区内任意两点之间的分差。 45 | * 46 | * 因此这道题目是一个 带权并查集 的考察。 47 | * 当两位学生在同一个连通区内(具有同一个 parent)时,他们是可以被比较的,否则无法被比较。 48 | * 权值数组 rank[i] 定义为:学生 ith 低于 parent 学生分数的分值大小,即 rank[i] = iParent - i 49 | * Union(a, b) 的时候,b所属的区域 成为 a所属区域 的子节点,同时我们也要计算出 bFather 的 rank 值。 50 | * parent[bFather] = aFather; 51 | * rank[bFather] = rank[a] - rank[b] + diff; 52 | * 关于 rank[bFather] 的计算我们可以做如下分析: 53 | * rank[a] = aFather - a; rank[b] = bFather - b; diff = a - b 54 | * 而我们要求的 rank[bFather] = aFather - bFather 55 | * 那么 rank[a] - rank[b] = aFather - bFather - a + b; 56 | * 经过等式变换后可得:rank[bFather] = aFather - bFather = rank[a] - rank[b] + diff 57 | * 58 | * 类似问题: 59 | * https://github.com/cherryljr/LintCode/blob/master/Deliver%20The%20Message.java 60 | */ 61 | 62 | import java.util.*; 63 | 64 | public class Main { 65 | public static void main(String[] args) { 66 | Scanner sc = new Scanner(System.in); 67 | int n = sc.nextInt(); 68 | int m = sc.nextInt(); 69 | int q = sc.nextInt(); 70 | UnionFind uf = new UnionFind(n + 1); 71 | 72 | int a, b, diff; 73 | for (int i = 0; i < m; i++) { 74 | a = sc.nextInt(); 75 | b = sc.nextInt(); 76 | diff = sc.nextInt(); 77 | uf.union(a, b, diff); 78 | } 79 | for (int i = 0; i < q; i++) { 80 | a = sc.nextInt(); 81 | b = sc.nextInt(); 82 | if (uf.compressedFind(a) == uf.compressedFind(b)) { 83 | System.out.println(uf.rank[b] - uf.rank[a]); 84 | } else { 85 | System.out.println(-1); 86 | } 87 | } 88 | 89 | sc.close(); 90 | } 91 | 92 | static class UnionFind { 93 | int[] parent; 94 | int[] rank; 95 | 96 | UnionFind(int n) { 97 | parent = new int[n]; 98 | rank = new int[n]; 99 | for (int i = 0; i < n; i++) { 100 | parent[i] = i; 101 | } 102 | } 103 | 104 | public int compressedFind(int index) { 105 | // 考虑到 rank[] 的影响,此处使用了 递归 写法 106 | // 大家也可以使用 Stack 改为非递归版本 107 | if (index == parent[index]) { 108 | return index; 109 | } else { 110 | // 预先保存 parent[index],以便 rank[index] 调用,不然之后会发生变化 111 | int pre = parent[index]; 112 | parent[index] = compressedFind(parent[index]); 113 | rank[index] = rank[index] + rank[pre]; 114 | return parent[index]; 115 | } 116 | } 117 | 118 | public void union(int a, int b, int diff) { 119 | int aFather = compressedFind(a); 120 | int bFather = compressedFind(b); 121 | if (aFather != bFather) { 122 | // 将 b 归入到 a 所属的区域中 123 | parent[bFather] = aFather; 124 | rank[bFather] = rank[a] - rank[b] + diff; 125 | } 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /分金条的最少花费.java: -------------------------------------------------------------------------------- 1 | /* 2 | 一块金条切成两半,是需要花费和长度数值一样的铜板的。 3 | 比如长度为20的 金条, 不管切成长度多大的两半, 都要花费20个铜板。 4 | 一群人想整分整块金 条, 怎么分最省铜板? 5 | 6 | 例如,给定数组{10,20,30}, 代表一共三个人, 整块金条长度为10+20+30=60. 金条要分成10,20,30三个部分。 7 | 如果,先把长度60的金条分成10和50, 花费60 再把长度50的金条分成20和30,花费50一共花费110铜板。 8 | 但是如果, 先把长度60的金条分成30和30, 花费60 再把长度30金条分成10和20, 花费30 一共花费90铜板。 9 | 10 | 求输入一个数组, 返回分割的最小代价。 11 | */ 12 | 13 | /** 14 | * Approach: Greedy 15 | * 这道题目实际上考察的是 哈夫曼编码 问题。而它就是一个 贪心算法。 16 | * 其做法是: 17 | * 1. 建立一个 小根堆; 18 | * 2. 每次取出最小的 两个数 将他们合并在一起(加起来)。 19 | * 加起来得到的这个值 cost,就是我们切分一次所需要的代价; 20 | * 3. 将 cost 返回小根堆中; 21 | * 4. 重复 2,3 步骤,直到小根堆中只剩下一个数。 22 | * 23 | * 贪心的证明大家可以搜索一下 Huffman Code 的证明, 24 | * 而我这边想要表达的是: 25 | * 当一个 总代价 是由 子代价 累加/累乘 计算而来的时候,我们可以尝试着考虑使用 哈夫曼编码 的方式进行解决。 26 | * 27 | * 参考资料: 28 | * https://www.youtube.com/watch?v=dM6us854Jk0&t=15s 29 | * https://www.geeksforgeeks.org/greedy-algorithms-set-3-huffman-coding/ 30 | */ 31 | import java.io.BufferedInputStream; 32 | import java.util.PriorityQueue; 33 | import java.util.Scanner; 34 | 35 | public class Main { 36 | public static void main(String[] args) { 37 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 38 | int n = sc.nextInt(); // 输出输入长度 39 | int[] arr = new int[n]; 40 | for (int i = 0; i < n; i++) { 41 | arr[i] = sc.nextInt(); 42 | } 43 | System.out.println(leastCost(arr)); 44 | } 45 | 46 | private static int leastCost(int[] arr) { 47 | PriorityQueue minHeap = new PriorityQueue<>(); 48 | for (int i : arr) { 49 | minHeap.add(i); 50 | } 51 | int sum = 0; 52 | int temp = 0; 53 | while (minHeap.size() > 1) { 54 | temp = minHeap.poll() + minHeap.poll(); 55 | sum += temp; 56 | minHeap.add(temp); 57 | } 58 | return sum; 59 | } 60 | } -------------------------------------------------------------------------------- /删数.java: -------------------------------------------------------------------------------- 1 | Josephus 问题 2 | 直接利用数学公式计算出来即可。 3 | 注意: 4 | 题目是从 0 开始报数,而调用的 Josephus 函数报数最少是从 1 开始的,故答案需要 -1 即可。 5 | 6 | /* 7 | 有一个数组a[N]顺序存放0~N-1,要求每隔两个数删掉一个数,到末尾时循环至开头继续进行,求最后一个被删掉的数的原始下标位置。 8 | 以8个数(N=7)为例:{0,1,2,3,4,5,6,7},0->1->2(删除)->3->4->5(删除)->6->7->0(删除),如此循环直到最后一个数被删除。 9 | 10 | 输入描述: 11 | 每组数据为一行一个整数n(小于等于1000),为数组成员数,如果大于1000,则对a[999]进行计算。 12 | 13 | 输出描述: 14 | 一行输出最后一个被删掉的数的原始下标位置。 15 | 16 | 输入例子1: 17 | 8 18 | 19 | 输出例子1: 20 | 6 21 | */ 22 | 23 | import java.util.*; 24 | 25 | public class Main{ 26 | public static void main(String[] args) { 27 | Scanner sc = new Scanner(System.in); 28 | while (sc.hasNext()) { 29 | int n = sc.nextInt(); 30 | if (n > 1000) { 31 | n = 999; 32 | } 33 | 34 | System.out.println(Josephus(n, 3, 1) - 1); 35 | } 36 | } 37 | 38 | /** 39 | * Josephus 环的一个O(N)算法 40 | * 41 | * @param n 总人数 42 | * @param m 数到m的人出列 43 | * @param k 开始报数人的编号 44 | * @return 最后一个出列的编号 45 | */ 46 | public static int Josephus(int n, int m, int k) { 47 | int ans = 0; 48 | for (int i = 2; i <= n; i++) { 49 | ans = (ans + m) % i; 50 | } 51 | // 返回最后一人的位置 52 | return ans + k == n ? n : (ans + k) % n; 53 | } 54 | } 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /删除元素构造回文串的最小代价.java: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个字符串s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢? 3 | 输出需要删除的字符个数。 4 | 5 | 输入描述: 6 | 输入数据有多组,每组包含一个字符串s,且保证:1<=s.length<=1000. 7 | 8 | 输出描述: 9 | 对于每组数据,输出一个整数,代表最少需要删除的字符个数。 10 | 11 | 示例1 12 | 输入 13 | abcda 14 | google 15 | 16 | 输出 17 | 2 18 | 2 19 | */ 20 | 21 | /** 22 | * 提到回文串,自然要利用回文串的特点,想到将源字符串逆转后, 23 | * 如果是“回文串”(不一定连续)的话,那么字符串的顺序相当于没有改变。 24 | * 求原字符串和其反串的最大公共子序列的长度 LCS(使用动态规划很容易求得), 25 | * 然后用原字符串的长度减去这个最大公共子串的长度就得到了最小编辑长度。 26 | */ 27 | import java.util.*; 28 | 29 | public class Main{ 30 | public static void main(String[] args){ 31 | Scanner scan = new Scanner(System.in); 32 | while(scan.hasNext()){ 33 | String str = scan.nextLine(); 34 | System.out.println(str.length() - LCS(str)); 35 | } 36 | } 37 | 38 | private static int LCS(String str) { 39 | StringBuilder sb = new StringBuilder(str); 40 | String strReverse = sb.reverse().toString(); 41 | 42 | int[][] dp = new int[str.length() + 1][str.length() + 1]; 43 | dp[0][0] = 0; 44 | 45 | for (int i = 1; i <= str.length(); i++) { 46 | for (int j = 1; j <= str.length(); j++) { 47 | if (str.charAt(i - 1) == strReverse.charAt(j - 1)) { 48 | dp[i][j] = dp[i - 1][j - 1] + 1; 49 | } else { 50 | dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); 51 | } 52 | } 53 | } 54 | 55 | return dp[str.length()][str.length()]; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /判断是否为完全二叉树.java: -------------------------------------------------------------------------------- 1 | 任意的一个二叉树,都可以补成一个满二叉树。但是这样中间就会有很多空洞。 2 | 在广度优先遍历的时候,如果是满二叉树,或者完全二叉树,这些空洞是在广度优先的遍历的末尾。 3 | 所以,但我们遍历到空洞的时候,整个二叉树就已经遍历完成了。 4 | 而如果,是非完全二叉树,我们遍历到空洞的时候,就会发现,空洞后面还有没有遍历到的值。 5 | 这样,只要根据是否遍历到空洞,整个树的遍历是否结束来判断是否是完全的二叉树。 6 | 7 | /* 8 | 给定一颗树,判断其是否为完全二叉树 9 | */ 10 | 11 | import java.util.*; 12 | 13 | class TreeNode { 14 | int val; 15 | TreeNode left = null; 16 | TreeNode right = null; 17 | public TreeNode(int val) { 18 | this.val = val; 19 | } 20 | } 21 | 22 | public class CheckCompletion { 23 | public boolean checking(TreeNode root) { 24 | Queue queue = new LinkedList(); 25 | TreeNode node; 26 | 27 | // 进行广度优先遍历(层次遍历),并把NULL节点也放入队列 28 | queue.add(root); 29 | while ((node = queue.poll()) != null) { 30 | queue.offer(node.left); 31 | queue.offer(node.right); 32 | } 33 | 34 | // 判断是否还有未被访问到的节点 35 | while (!queue.isEmpty()) { 36 | node = queue.poll(); 37 | // 有未访问到的的非NULL节点,则树存在空洞,为非完全二叉树 38 | if (node != null) { 39 | return false; 40 | } 41 | } 42 | 43 | return true; 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /双拆分数.java: -------------------------------------------------------------------------------- 1 | /* 2 | 对于一个数字串 s,若能找到一种将其分成左右两个非空部分 s1,s2 的方案,使得: 3 |     1、s1,s2 均无前导零 4 |     2、存在两个正整数 a,b,使得 b 整除 a,且 a/b=s1, a*b=s2 5 | 那么我们记这是一个合法的分法。特别地,如果一个串有两个或更多个不同的合法的分法,那么我们称这个数字串是双拆分数字串。 6 | 给定一个 n,要求构造一个长度恰为 n 的双拆分数字串。如果无解,输出 -1。 7 | 8 | 输入描述: 9 | 输入仅一行一个正整数 n(1 <= n <= 300)。 10 | 输出描述: 11 | 仅一行一个数字串或者 -1。 12 | 示例1 13 | 输入 14 | 8 15 | 输出 16 | 24419764 17 | */ 18 | 19 | import java.util.*; 20 | 21 | public class Main { 22 | public static void main(String[] args) { 23 | Scanner sc = new Scanner(System.in); 24 | int num = sc.nextInt(); 25 | if (num <= 3) { 26 | System.out.println(-1); 27 | } 28 | 29 | if ((num & 1) == 0) { 30 | System.out.print("1144"); 31 | num -= 4; 32 | } else { 33 | System.out.print("16400"); 34 | num -= 5; 35 | } 36 | while (num-- > 0) { 37 | System.out.print("0"); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /合唱团.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/合唱团.java -------------------------------------------------------------------------------- /含有连续两个str作为子串的最短字符串.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述: 3 | 给定一个字符串s,请计算输出含有连续两个s作为子串的最短字符串。 4 | 注意两个s可能有重叠部分。例如,”ababa”含有两个aba。 5 | 6 | 输入描述: 7 | 输入包括一个字符串s,字符串长度length(1 0) { 71 | // 当着两个字符 不相等 时,cn向前跳跃到 next[cn] 的位置,去寻找长度更短的相同前后缀。 72 | cn = next[cn]; 73 | } else { 74 | // cn<=0; 此时说明前面已经没有相同前后缀了,即 cn 已经没办法再跳跃了, 75 | // 此时 pos 对应的 next[pos] 值为 0 (无相同前后缀) 76 | next[pos++] = 0; 77 | } 78 | } 79 | 80 | return next[next.length - 1]; 81 | } 82 | 83 | public static void main(String[] args) { 84 | String test1 = "a"; 85 | System.out.println(shortestHaveTwice(test1)); 86 | 87 | String test2 = "aa"; 88 | System.out.println(shortestHaveTwice(test2)); 89 | 90 | String test3 = "ab"; 91 | System.out.println(shortestHaveTwice(test3)); 92 | 93 | String test4 = "abcdabcd"; 94 | System.out.println(shortestHaveTwice(test4)); 95 | 96 | String test5 = "abracadabra"; 97 | System.out.println(shortestHaveTwice(test5)); 98 | 99 | } 100 | 101 | } 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /吸血鬼数.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/吸血鬼数.java -------------------------------------------------------------------------------- /大数据里找上中位数.java: -------------------------------------------------------------------------------- 1 | /* 2 | 40亿 大整数,组成了一个大文件。 3 | 想找到其中的 上中位数该怎么办? 4 | 内存:10MB,怎么办? 5 | 内存:200K,怎么办? 6 | 内存:有限几个字符,怎么办? 7 | 条件:按行读取文件,读取操作不占用内存。 8 | */ 9 | 10 | 应该具备的能力: 11 | 2^k = ? 应该都能够熟记,达到反射性反应的程度。 12 | 字节数 对应计算机中的 容量(T, G, M, K) 13 | 14 | 内存只有 10MB 的情况 15 | 接下来我们来解题: 16 | 看到大数据容量限制的,首先想到的是从范围入手。 17 | 1. 数据是 有符号? / 无符号? 18 | 2. 我们知道一个 4字节的无符号整数 范围为:0~42亿 19 | 那么我们可以用一个 unsigned int 来表示一个数出现的次数 20 | (一个数最多出现 40亿 次,故能够表示,不会溢出) 21 | 3. 我们来计算 10MB 内存可以存几个 unsigned int 的数。 => 250万 22 | 因此我们知道 10MB 内存足够我们在 250W 范围内进行 精细的词频统计(每个数出现几次) 23 | 4. 0~42亿 内总共有 1680个 250W 范围的段。因此我们将 42亿 的范围按照 250W 进行分段。([0, 250w), [250w,500w)...) 24 | 建立一个 1680 的数组,遍历大文件一次,来统计每个段中出现数的个数。 25 | count[i] 就表示在第 i 段的 连续的250W 的范围内,出现了多少个数。 26 | 比如:一个数字值为10亿,那么它应该就在 count[400] 这个段中,那么进行操作 count[400]++ 即可。 27 | 5. 对 count 进行累加,直到 sum >= 20亿。这样我们就能确定第 20亿 个数是来自哪个范围的。 28 | 6. 释放该数组,再次遍历大文件,利用 10MB 的空间对该范围内的数进行 精细的词频统计。这样便能够找到中位数了。 29 | 由此可见,以上做法是借用了 桶排序 的思想。 30 | 31 | 总结: 32 | 1. 利用限制的范围计算出我们能够在多大的范围内进行 精细的词频统计。 33 | 2. 利用 精细的范围 对整体范围进行划分,建立一个粗略的统计数组。 34 | 3. 遍历大文件,找出中位数粗略的范围。 35 | 4. 对该粗略的范围进行精细的统计。 36 | 37 | 内存只有 20K 的情况: 38 | 我们依然用上面的方法来分析: 39 | 20K 的内存可以支持 5000范围大小 的精细词频统计。 40 | 然后我们用 40亿 / 5000 => 发现该数值已经怨愿你大于 5000 了,我们根本无法进行粗略范围的统计。 41 | 于是,我们不妨逆过来思考。 42 | 直接从 粗略的范围 出发进行统计。将其分成 5000 份。 43 | 我们依然能够得知 中位数 是在哪个部分。然后看该范围能否被精细统计。 44 | 若不能一直循环下去。 45 | 46 | 内存只有 有限几个字符 的情况(比如就8个字节,两个变量): 47 | 采用二分的方法。最多二分 32 次,即需要读 32 次文件。 48 | 1. 首先对 0~42 亿二分(注意是对整个范围进行二分),用一个变量 k 记下当前的值是多少。 49 | 遍历文件一边遍,计算当前小于当前值得数据个数 n。 50 | 2. 若 n < 21亿,该数在小半部分。若 n > 21亿,该数在大部分。 51 | 以此循环下去。 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /奇虎360_偶串.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 一个字符串S是偶串当且仅当S中的每一个字符都出现了偶数次。如字符串”aabccb”是一个偶串,因为字符a,b,c都出现了两次。 4 | 而字符串”abbcc”不是偶串,因为字符a出现了一次。 5 | 6 | 现在给出一个长度为n的字符串T=t1,t2,t3,…,tn。字符串的子串为其中任意连续一段。 7 | T长度为1的子串有n个,长度为2的子串有n-1个,以此类推,T一共有n(n+1)/2个子串。 8 | 给定T,你能算出它有多少个子串是偶串吗? 9 | 10 | 输入 11 | 输入一个字符串T,T中只有小写字母。T的长度不超过100000。 12 | 13 | 样例输入 14 | abbc 15 | 16 | 输出 17 | 输出一个数,T的所有子串中偶串的个数。 18 | 19 | 样例输出 20 | 1 21 | 22 | 时间限制 23 | C/C++语言:2000MS其它语言:4000MS 24 | 内存限制 25 | C/C++语言:65536KB其它语言:589824KB 26 | */ 27 | 28 | /** 29 | * Approach: PreXOR + HashMap 30 | * 属于利用 PreSum + HashMap 解决 Subarray 问题较为经典的改编。 31 | * 一开始确实较难想出最优解,但是可以通过一步步进行优化,从而达到目标。 32 | * 关键点在于: 33 | * 我们并不关心某一段区间内某个字符的出现个数具体为多少个, 34 | * 而是仅仅需要 奇/偶 这个信息。因此可以将其优化为 0/1 的表示。 35 | * 然后在通过异或运算进行改变状态。 36 | * 从而达到最终优化的目的。 37 | * 38 | * 时间复杂度:O(n) 39 | * 空间复杂度:O(n) 40 | * 41 | * 参考资料: 42 | * http://exercise.acmcoder.com/online/online_judge_answer_pdf?ques_id=3980&konwledgeId=42 43 | * Maximum Size Subarray Sum Equals k: 44 | * https://github.com/cherryljr/LintCode/blob/master/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java 45 | */ 46 | 47 | import java.util.*; 48 | 49 | public class Main { 50 | public static void main(String[] args) { 51 | Scanner sc = new Scanner(System.in); 52 | while (sc.hasNext()) { 53 | String str = sc.next(); 54 | Map map = new HashMap<>(); 55 | // 初始化表示所有的字母都出现0(偶数)次 56 | map.put(0, 1); 57 | 58 | // 用低26bit表示下标为[0,i]的子串所拥分别有的字母是偶数个(0)还是奇数个(1)。 59 | int preXOR = 0; 60 | int count = 0; 61 | for (int i = 0; i < str.length(); i++) { 62 | // 求加入当前字符后,拥有字母个数的奇偶性。如果异或后是0,表示加入该字符后有偶数个字母,反之为奇数个 63 | preXOR ^= (1 << (str.charAt(i) - 'a')); 64 | if (map.containsKey(preXOR)) { 65 | count += map.get(preXOR); 66 | map.put(preXOR, map.get(preXOR) + 1); 67 | } else { 68 | map.put(preXOR, 1); 69 | } 70 | } 71 | 72 | System.out.println(count); 73 | } 74 | sc.close(); 75 | } 76 | } -------------------------------------------------------------------------------- /奇虎360_剪气球串.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 小明买了一些彩色的气球用绳子串在一条线上,想要装饰房间,每个气球都染上了一种颜色,每个气球的形状都是各不相同的。 4 | 我们用1到9一共9个数字表示不同的颜色,如12345则表示一串5个颜色各不相同的气球串。但小明希望得到不出现重复颜色的气球串, 5 | 那么现在小明需要将这个气球串剪成多个较短的气球串,小明一共有多少种剪法? 6 | 如原气球串12345的一种是剪法是剪成12和345两个气球串。 7 | 8 | 注意每种剪法需满足最后的子串中气球颜色各不相同(如果满足该条件,允许不剪,即保留原串)。 9 | 两种剪法不同当且仅当存在一个位置,在一种剪法里剪开了,而在另一种中没剪开。详见样例分析。 10 | 11 | 输入 12 | 第一行输入一个正整数n(1≤n≤100000),表示气球的数量。 13 | 第二行输入n个整数a1,a2,a3...an,ai表示该气球串上第i个气球的颜色。对于任意i,有1≤ai≤9。 14 | 15 | 样例输入 16 | 3 17 | 1 2 3 18 | 19 | 输出 20 | 输出一行,第一行输出一个整数,表示满足要求的剪法,输出最终结果除以1000000007后的余数。 21 | 22 | 样例输出 23 | 4 24 | 25 | 时间限制 26 | C/C++语言:2000MS其它语言:4000MS 27 | 内存限制 28 | C/C++语言:131072KB其它语言:655360KB 29 | */ 30 | 31 | /** 32 | * Approach: DP 33 | * 类似爬台阶的做法,属于 Climb Stairs 的升级版本。 34 | * dp[i]表示前i个气球组合的最大剪枝方法数, 35 | * 然后遍历当前位置的前 j 个位置作为断点,将所有可能的方案数(颜色不重复)加起来, 36 | * 就是当前方案的方案总数了。因为气球的颜色总共只有 10 中, 37 | * 所以向前遍历的步数不会超过 10. 38 | * 39 | * 时间复杂度:O(10 * N) => O(N) 40 | * 空间复杂度:O(N) 41 | */ 42 | 43 | import java.util.*; 44 | 45 | public class Main { 46 | public static void main(String[] args) { 47 | Scanner sc = new Scanner(System.in); 48 | while (sc.hasNext()) { 49 | int N = sc.nextInt(); 50 | int[] balloons = new int[N]; 51 | for (int i = 0; i < N; i++) { 52 | balloons[i] = sc.nextInt(); 53 | } 54 | // dp[i]表示前i个气球组合的最大剪枝方法数 55 | int[] dp = new int[N + 1]; 56 | 57 | dp[0] = 1; 58 | for (int i = 1; i <= N; i++) { 59 | int[] count = new int[10]; 60 | // 把前面不相同的颜色的气球的剪枝数加起来即可 61 | // j向前遍历最多不会超过 10.(气球颜色只有 10 种) 62 | for (int j = i - 1; j >= 0; j--) { 63 | // 如果出现重复颜色,直接break 64 | if (++count[balloons[j]] > 1) { 65 | break; 66 | } 67 | dp[i] = (dp[i] + dp[j]) % 1000000007; 68 | } 69 | } 70 | System.out.println(dp[N]); 71 | } 72 | sc.close(); 73 | } 74 | } -------------------------------------------------------------------------------- /奇虎360_跑步.java: -------------------------------------------------------------------------------- 1 | /* 2 | 小明同学喜欢体育锻炼,他常常去操场上跑步。跑道是一个圆形,在本题中, 3 | 我们认为跑道是一个半径为R的圆形,设圆心的坐标为原点(0,0)。 4 | 小明跑步的起点坐标为(R,0),他沿着圆形跑道跑步,而且一直沿着一个方向跑步。 5 | 回到家后,他查看了自己的计步器,计步器显示他跑步的总路程为L。 6 | 7 | 小明想知道自己结束跑步时的坐标,但是他忘记自己是沿着顺时针方向还是逆时针方向跑的了。 8 | 他想知道在这两种情况下的答案分别是多少。 9 | 10 | 输入 11 | 输入两个整数L,R (1<=L,R<=100)。 12 | 13 | 样例输入 14 | 1 2 15 | 16 | 输出 17 | 输出两行,每行两个数,用空格隔开。第一行的两个数为顺时针情况下结束位置的坐标, 18 | 第二行是逆时针情况下结束位置的坐标。所有数据小数点后四舍五入保留3位。 19 | 20 | 样例输出 21 | 1.755 -0.959 22 | 1.755 0.959 23 | 24 | 时间限制 25 | C/C++语言:2000MS其它语言:4000MS 26 | 内存限制 27 | C/C++语言:65536KB其它语言:589824KB 28 | 29 | http://exercise.acmcoder.com/online/online_judge_ques?ques_id=3861&konwledgeId=42 30 | */ 31 | 32 | /** 33 | * Approach: Mathematics 34 | * 初中数学知识... 35 | * 36 | * http://exercise.acmcoder.com/online/online_judge_answer_pdf?ques_id=3861&konwledgeId=42 37 | */ 38 | 39 | import java.util.Scanner; 40 | 41 | public class Main { 42 | public static void main(String[] args) { 43 | Scanner sc = new Scanner(System.in); 44 | while (sc.hasNext()) { 45 | double L = sc.nextInt(); 46 | double R = sc.nextInt(); 47 | double x = R * Math.cos(L / R); 48 | double y = R * Math.sin(L / R); 49 | // 顺时针结果 50 | System.out.printf("%.3f %.3f\n", x, -y); 51 | // 逆时针结果 52 | System.out.printf("%.3f %.3f\n", x, y); 53 | } 54 | sc.close(); 55 | } 56 | } -------------------------------------------------------------------------------- /字符串中英文字符的个数.java: -------------------------------------------------------------------------------- 1 | 该题目考查的是基础。 2 | 因为要求时间复杂度为 O(1).并且题目中说明只含有 中文 和 英文 两种字符。 3 | 所以我们想到了使用字节占用空间的方法。 4 | 一个英文字符占 1 个字节,一个中文字符占 2 个字节。 5 | 利用这点我们便可以计算出结果。 6 | 比如:字符串中有 n 个字符,占用了 m 个字节。 7 | 那么中文有:m - n 个; 英文有:2*n-m 个 8 | 9 | /* 10 | 给定一个字符串,里面只含有 中文字符 和 英文字符。 11 | 请分别求出 中文字符 和 英文字符 的个数。 12 | 13 | 要求:不使用 循环,时间复杂度为 O(1) 14 | */ 15 | 16 | public class Solution { 17 | public static void main(String[] args) { 18 | String a = "test测试cherryljr樱花"; 19 | int size = a.length(); 20 | int bytelen = a.getBytes().length; 21 | // System.out.println(size); 22 | // System.out.println(bytelen); 23 | System.out.println("英文字符个数:" + (size * 2 - bytelen)); 24 | System.out.println("中文字符个数:" + (bytelen - size)); 25 | } 26 | } -------------------------------------------------------------------------------- /字符串的交错组成.java: -------------------------------------------------------------------------------- 1 | /* 2 | 【题目】 3 | 给定三个字符串str1、str2和aim,如果aim包含且仅包含来自str1和str2的所有字符, 4 | 而且在aim中属于str1的字符之间保持原来在str1中的顺序,属于str2的字符之间保持原来在str2中的顺序, 5 | 那么称aim是str1和str2的交错组成。 6 | 实现一个函数,判断aim是否是str1和str2交错组成。 7 | 【举例】 8 | str1="AB", str2="12"。 那么"AB12"、 "A1B2"、 "A12B"、 "1A2B"和"1AB2"等都 9 | 是str1和str2的交错组成。 10 | */ 11 | 12 | /** 13 | * Approach: DP 14 | * 很明显的 DP 问题。 15 | * 当 str1 中的位置 i 与 str2 中的位置 j 确定之后,其结果就已经是确定了的(无后效性) 16 | * 当前位置的值 dp[i][j] 依赖于 dp[i-1][j] 与 dp[i][j-1] 17 | * 只需要 i+j-1 之前部分能够匹配并且 str1.charAt(i-1) == aim.charAt(i+j-1) 18 | * 或者是 str2.charAt(j-1) == aim.charAt(i+j-1) 19 | * 那么当前状态就能够成立。 20 | */ 21 | public class Main { 22 | 23 | public static boolean isCross(String str1, String str2, String aim) { 24 | if (str1 == null || str2 == null || aim == null) { 25 | return false; 26 | } 27 | if (aim.length() != str1.length() + str2.length()) { 28 | return false; 29 | } 30 | 31 | boolean[][] dp = new boolean[str1.length() + 1][str2.length() + 1]; 32 | // Initialize 33 | dp[0][0] = true; 34 | for (int i = 1; i <= str1.length(); i++) { 35 | if (str1.charAt(i - 1) != aim.charAt(i - 1)) { 36 | break; 37 | } 38 | dp[i][0] = true; 39 | } 40 | for (int j = 1; j <= str2.length(); j++) { 41 | if (str2.charAt(j - 1) != aim.charAt(j - 1)) { 42 | break; 43 | } 44 | dp[0][j] = true; 45 | } 46 | 47 | // Function 48 | for (int i = 1; i <= str1.length(); i++) { 49 | for (int j = 1; j <= str2.length(); j++) { 50 | if ((str1.charAt(i - 1) == aim.charAt(i + j - 1) && dp[i - 1][j]) 51 | || (str2.charAt(j - 1) == aim.charAt(i + j - 1) && dp[i][j - 1])) { 52 | dp[i][j] = true; 53 | } 54 | } 55 | } 56 | 57 | return dp[str1.length()][str2.length()]; 58 | } 59 | 60 | public static void main(String[] args) { 61 | String str1 = "1234"; 62 | String str2 = "abcd"; 63 | String aim = "1a23bcd4"; 64 | System.out.println(isCross(str1, str2, aim)); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /寻宝.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 牛牛解出了卷轴隐藏的秘密,来到了一片沼泽地。这里有很多空地,而面试直通卡可能埋在任意一块空地中,好在亮亮发现了一堆木材, 4 | 他可以将木材铺在两个空地之间的沼泽地上。 5 | 因为亮亮不知道面试直通卡具体在哪一块空地中,所以必须要保证任意一块空地对于亮亮来说是可以抵达的。 6 | “怎么还有鳄鱼!没办法,看来有些空地不能直接到达了。” 亮亮虽然没有洁癖,但是沼泽地实在太臭了,所以亮亮不会循环利用木材。 7 | 而且木材不能拼接在一起使用,所以亮亮必须要知道在耗费木材最少的情况下,最长的那根木材至少需要多长。 8 | 9 | 输入描述: 10 | 第一行包含两个整数N(1≤N≤10000),M(1≤M≤1000000)。N表示公有N块空地。 11 | 接下来M行,每行包含三个整数P(1≤P≤N),Q(1≤Q≤N),K代表P,Q两个间没有鳄鱼,需要耗费K的木材。 12 | 13 | 输出描述: 14 | 一个整数,即耗费木材最少的情况下,最长的那根木材长度。 15 | 16 | 示例1 17 | 输入 18 | 4 3 19 | 1 2 1 20 | 2 3 1 21 | 3 4 2 22 | 输出 23 | 2 24 | */ 25 | 26 | /** 27 | * Approach: Union Find 28 | * 主要利用了 Union Find 并查集 这个数据结构来解 29 | * 不了解这个数据结构的可以参考 LintCode 上面一道关于 并查集 的入门问题:Find the Weak Connected Component in the Directed Graph 30 | * 解析:https://github.com/cherryljr/LintCode/blob/master/Find%20the%20Weak%20Connected%20Component%20in%20the%20Directed%20Graph.java 31 | * 32 | * 接下来对本道题目进行分析: 33 | * 想到使用 Union Find 基本上这道题就做出来了起码 70% 吧。 34 | * 那么为什么我会想到使用这个数据结构呢? 35 | * 突破在于:题目中涉及到了多次 连接 一个个孤立点,并形成联通区 的这么一个情况。(牛牛用木板架桥) 36 | * 这类题目由于会设计到 联通/合并 问题,所以通常使用 并查集 来解决。 37 | * 并且 Union Find 有着非常好的时间复杂度。 38 | * 题目说因为不知道宝藏放在哪里...所以这里也透露出一个信息: 39 | * 需要我们将所有的 岛屿 都通过木板连接起来,并且使得连接代价最小。 40 | * 然后要求 所有连接板中最长的长度。 41 | * 因此,很明显我们只需要在 union 过程中加入判断条件即可。或者直接改写 union 函数 42 | * (因为我直接用的模板就懒得改了,也不推荐大家改就是了,功能尽量分开一些也方便 Debug) 43 | * 44 | * 具体实现方法为: 45 | * 首先初始化一个大小为 n 的 并查集结构。 46 | * 然后对输入的边,按照权重 从小到大 进行排序,以保证我们使用最少的木板。 47 | * 然后我们遍历每一条边,如果发现边两头的岛屿 不在同一块连通区 上,我们就把它们 union 起来。 48 | * 同时记得更新 最长木板的长度即可。 49 | * 50 | * 时间复杂度为:排序O(nlogn) + 并查集的n次操作O(n) => O(nlogn) 51 | * 52 | * OJ地址:https://www.nowcoder.com/questionTerminal/59aff3b7a9094432893302c9ee7794e8 53 | */ 54 | 55 | import java.util.*; 56 | 57 | public class Main { 58 | 59 | static class Edge { 60 | public int x; 61 | public int y; 62 | public int val; 63 | 64 | public Edge(int x, int y, int val) { 65 | this.x = x; 66 | this.y = y; 67 | this.val = val; 68 | } 69 | } 70 | 71 | static int[] uf; 72 | 73 | public static void main(String[] arg) { 74 | Scanner sc = new Scanner(System.in); 75 | int n = sc.nextInt(); 76 | int m = sc.nextInt(); 77 | Edge[] edges = new Edge[m]; 78 | for (int i = 0; i < m; i++) { 79 | int x = sc.nextInt(); 80 | int y = sc.nextInt(); 81 | int val = sc.nextInt(); 82 | edges[i] = new Edge(x, y, val); 83 | } 84 | System.out.println(getAnswer(edges, n, m)); 85 | } 86 | 87 | private static int getAnswer(Edge[] edges, int n, int m) { 88 | int rst = 0; 89 | uf = new int[n + 1]; 90 | // 初始化并查集,使得每个点刚开始的 parent 指向其自身 91 | initialize(n); 92 | // 对连接岛屿所需要的木板按从小到大的长度排序(边权重低的在前面) 93 | Arrays.sort(edges, (e1, e2) -> (e1.val - e2.val)); 94 | for (int i = 0; i < m; i++) { 95 | int rootA = findParent(edges[i].x); 96 | int rootB = findParent(edges[i].y); 97 | if (rootA != rootB) { 98 | // 如果两个岛的 root 节点不同,说明不在同一个联通区内,因此将它们 union 起来 99 | // 同时如果遇到 长度更长的木板,需要更新结果 100 | if (rst < edges[i].val) { 101 | rst = edges[i].val; 102 | } 103 | union(rootA, rootB); 104 | } 105 | } 106 | return rst; 107 | } 108 | 109 | private static void initialize(int n) { 110 | for (int i = 1; i <= n; i++) { 111 | uf[i] = i; 112 | } 113 | } 114 | 115 | private static int findParent(int index) { 116 | while (uf[index] != index) { 117 | uf[index] = uf[uf[index]]; 118 | index = uf[index]; 119 | } 120 | return index; 121 | } 122 | 123 | private static void union(int x, int y) { 124 | // 尽量维持树状结构的平衡 125 | if (x > y) { 126 | uf[x] = y; 127 | } else { 128 | uf[y] = x; 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /小Ho的防护盾.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目:小Ho的防护盾 3 | 时间限制:10000ms 4 | 单点时限:1000ms 5 | 内存限制:256MB 6 | 描述 7 | 小Ho的虚拟城市正在遭受小Hi的攻击,小Hi用来攻击小Ho城市的武器是一艘歼星舰, 8 | 这艘歼星舰会以T(T为大于0的整数)个单位时间的间隔向小Ho的城市轰击。歼星舰总共有N枚炮弹,其中第i枚会造成Ai点伤害值。 9 | 10 | 幸好小Ho的城市有K层护盾,每层护盾可以抵挡M点伤害。当某次轰击使得伤害值达或超过M时,该层护盾就会被击碎; 11 | 该次轰击溢出的伤害不会作用于下一层护盾;下一次轰击将由下一层护盾承受。 12 | 13 | 同时,受损但尚未被击碎护盾会以每单位时间减少1点伤害值的速度修复自己,直到伤害值降为0。 14 | 这就意味着小Hi的攻击间隔T越大,小Ho撑过这N枚炮弹的可能性就越大。 15 | 16 | 那么问题来了,小Hi的攻击间隔T至少需要是多少,小Ho城市的防护护盾才能不被全部击破? 17 | 18 | 为了使题目不存在歧义,规定: 19 | 小Hi的第i次攻击发生在时刻(i-1)*T 20 | 小Ho的第i次修复发生在时刻i-0.5 21 | 22 | 输入 23 | 第一行包含3个整数N、M和K,意义如前文所述。 24 | 第二行包含N个整数A1 - AN,表示小Hi第i枚炮弹的伤害值。 25 | 对于30%的数据,满足N<=100 26 | 对于100%的数据,满足1<=N<=100000 27 | 对于100%的数据,满足1<=K<=10, 1<=Ai, M<=100000 28 | 29 | 输出 30 | 输出使得小Ho城市的防护护盾不被全部击破的小Hi攻击间隔的最小值。如果不存在这样的T,则输出-1。 31 | 32 | 样例输入 33 | 3 5 1 34 | 3 3 3 35 | 样例输出 36 | 3 37 | 38 | OJ地址:https://hihocoder.com/contest/hiho188/problem/1 39 | */ 40 | 41 | /** 42 | * Approach: Binary Search 43 | * 攻击间隔越大,护盾可能的回血就越多。回血上限为 M. 44 | * 如果小Hi的攻击中 伤害值大于等于M的炮弹 少于K发,那么如果攻击间隔过大,即便N发总伤害超过KM,也可能最终不能击破全部护盾。 45 | * 题目要求出可以使护盾不被全部击破的攻击间隔最小值。这要求我们能够挖掘出一个隐藏信息: 46 | * 因为护盾回复的速度为每秒钟 1 点,所以这也就意味着攻击间隔 time,只有在范围 [1...M] 上才有意义。 47 | * 低于 1,则说明全部炮弹一次性发射,这才题目中是禁止的(参考题目规定) 48 | * 大于 M,则说明当前护盾的 HP 已经回满了,时间再长这个值也不会增加,因此没有考虑的必要。 49 | * 50 | * 那么根据以上分析 最短攻击间隔time,显然可以利用 二分法 进行求解。 51 | * 具体操作为: 52 | * 根据二分法(求下界)获得当前的攻击间隔 time,然后判断遍历一遍所有炮弹的伤害,看是否能够击破所有护盾即可。 53 | * 如果不能全部击破护盾,则 right = mid (right边界缩小向左靠),否则 left = mid + 1 (mid 必定不符合条件) 54 | * 因此我们要找的就是 第一个大于 临界值target 的时间(临界值指的是时间间隔 <= target 的话,护盾都将被击破) 55 | * 而对于炮弹攻击护盾的过程可以直接运算模拟即可。时间复杂度为 O(n) 56 | * 因此总体时间复杂度为 O(nlogm) 57 | */ 58 | import java.util.*; 59 | 60 | public class Main { 61 | static int n, m, k; 62 | static int[] shells; 63 | 64 | public static void main(String[] args) { 65 | Scanner sc = new Scanner(System.in); 66 | n = sc.nextInt(); 67 | m = sc.nextInt(); 68 | k = sc.nextInt(); 69 | shells = new int[n]; 70 | for (int i = 0; i < n; i++) { 71 | shells[i] = sc.nextInt(); 72 | } 73 | 74 | int count = 0; 75 | // 首先遍历整个炮弹数组,如果发现伤害值大于护盾M的炮弹个数 >= 护盾个数 76 | // 直接输出 -1 (恢复时间再久都没用) 77 | for (int shell : shells) { 78 | if (shell >= m) { 79 | count++; 80 | } 81 | if (count >= k) { 82 | System.out.println(-1); 83 | return; 84 | } 85 | } 86 | 87 | // 二分法 寻找最短输出时间间隔 (注意:left从是 1 开始) 88 | int left = 1, right = m; 89 | while (left < right) { 90 | int mid = left + ((right - left) >> 1); 91 | if (!canDestroy(mid)) { 92 | // 无法击破全部护盾 93 | right = mid; 94 | } else { 95 | // 能够击破全部护盾 96 | left = mid + 1; 97 | } 98 | } 99 | 100 | // 最后检查能否击破全部护盾 101 | if (canDestroy(left)) { 102 | System.out.println(-1); 103 | } else { 104 | System.out.println(left); 105 | } 106 | } 107 | 108 | private static boolean canDestroy(int time) { 109 | int leftShields = k; 110 | int hp = m; 111 | for (int shell : shells) { 112 | if (shell >= hp) { 113 | // 伤害值大于护盾值时,直接击破 114 | // 更新剩余护盾个数与护盾值 115 | leftShields--; 116 | hp = m; 117 | } else { 118 | // 如果未能击破,则扣去攻击值,并加上回复值 119 | hp -= shell; 120 | hp = hp + time >= m ? m : hp + time; 121 | } 122 | if (leftShields == 0) { 123 | // 无剩余护盾,代表能够击破全部护盾 124 | return true; 125 | } 126 | } 127 | return false; 128 | } 129 | 130 | } -------------------------------------------------------------------------------- /小易喜欢的数列.java: -------------------------------------------------------------------------------- 1 | /* 2 | 小易非常喜欢拥有以下性质的数列: 3 | 1、数列的长度为n 4 | 2、数列中的每个数都在1到k之间(包括1和k) 5 | 3、对于位置相邻的两个数A和B(A在B前),都满足(A <= B)或(A mod B != 0)(满足其一即可) 6 | 例如,当n = 4, k = 7 7 | 那么{1,7,7,2},它的长度是4,所有数字也在1到7范围内,并且满足第三条性质,所以小易是喜欢这个数列的 8 | 但是小易不喜欢{4,4,4,2}这个数列。小易给出n和k,希望你能帮他求出有多少个是他会喜欢的数列。 9 | 10 | 输入描述: 11 | 输入包括两个整数n和k(1 ≤ n ≤ 10, 1 ≤ k ≤ 10^5) 12 | 13 | 输出描述: 14 | 输出一个整数,即满足要求的数列个数,因为答案可能很大,输出对1,000,000,007取模的结果。 15 | 16 | 示例1 17 | 输入 18 | 2 2 19 | 输出 20 | 3 21 | 22 | OJ地址:https://www.nowcoder.com/questionTerminal/49375dd6a42d4230b0dc4ea5a2597a9b 23 | */ 24 | 25 | /** 26 | * Approach: Matrix DP 27 | * dp[i][j] 表示:长度为 i 时,以数值 j 结尾的符合要求的数列个数。 28 | * 那么结果就应该是 sum(dp[n][1...k]) 即矩阵的最后一行数值之和。 29 | * Base Case 为 dp[1][1...k] = 1,此状态不依赖与任何其他状态。 30 | * 因为符合条件的数列要求为:A <= B | A % B != 0 31 | * 因此 dp[i][j] 所依赖的状态为上一行中加上 j 后仍然符合要求的状态。 32 | * 即 dp[i][j] = sum(dp[i-1][m]) (m代表符合状态的上一行数列中的最后一个数值) 33 | * 由此分析下来总体时间复杂度为:O(n*k^2) 34 | * 根据题目的数据:n = 10, k = 10^5 => n*k^2 = 10^11 > 10^8 35 | * 一秒是绝对跑不完的,肯定要爆...因此我们必须对此进行一个时间复杂度上面的优化。 36 | * 37 | * 这里使用到了类似 筛法求素数(当然Sieve Method还要牛逼哈) 的做法对 sum(dp[i-1][m]) 的过程进行了一个优化。 38 | * 因为当我们需要加入 j 的时候,从 1~2*j-1 的部分必定是符合条件的; 39 | * 因此我们可以直接将 dp[i-1][0...j] 的值直接加起来作为 dp[i][j] 的一个部分。 40 | * 那么对于其他的值呢?我们可以从其中剔除掉所有 j 的倍数就可以了(A % B == 0 不符合条件) 41 | * 因此我们可以事先就计算出 dp[i-1][1...k] 的和(计算 i 行的时候,该值可被重复利用), 42 | * 然后剔除掉不符合条件方案个数的 sum(dp[i-1][1...k]) 就是 dp[i][j] 的值了。 43 | * 使用该做法可以将原本筛选需要花费 O(n) 时间复杂度优化到 O(logn) 级别。 44 | * 因此总体时间复杂度为:O(n*k*logk) 估算一下完全是符合要求的。 45 | */ 46 | 47 | import java.io.BufferedInputStream; 48 | import java.util.Arrays; 49 | import java.util.Scanner; 50 | 51 | public class Main { 52 | public static final int MOD = 1000000007; 53 | 54 | public static void main(String[] args) { 55 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 56 | int n = sc.nextInt(); 57 | int k = sc.nextInt(); 58 | int[][] dp = new int[n + 1][k + 1]; 59 | 60 | // Initialize 61 | // 长度只有 1 的时候,不管结尾数值是什么,只有 1 种数列 62 | Arrays.fill(dp[1], 1); 63 | 64 | // Function 65 | for (int i = 2; i <= n; i++) { 66 | // 事先计算出上一行 i-1 个元素组成数列的合法方案数,以作备用 67 | int sum = 0; 68 | for (int j = 1; j <= k; j++) { 69 | sum = (sum + dp[i - 1][j]) % MOD; 70 | } 71 | 72 | // O(klogk) 73 | for (int j = 1; j <= k; j++) { 74 | int invalid = 0; 75 | int mult = 2; 76 | while (mult * j <= k) { 77 | invalid = (invalid + dp[i - 1][mult * j]) % MOD; 78 | mult++; 79 | } 80 | // We'd better add MOD here 81 | dp[i][j] = (sum - invalid + MOD) % MOD; 82 | } 83 | } 84 | 85 | // Answer 86 | int rst = 0; 87 | for (int i = 1; i <= k; i++) { 88 | rst = (rst + dp[n][i]) % MOD; 89 | } 90 | System.out.println(rst); 91 | } 92 | } -------------------------------------------------------------------------------- /岛屿个数.java: -------------------------------------------------------------------------------- 1 | 这是一道求联通区的题目。使用到了DFS. 2 | 通过遍历整个矩阵,并递归调用 infect() 函数便可以找到所有的联通区。 3 | infect()函数的功能为以 m[i][j] 为基础向四周扩散, 4 | 判断四周是否存在值为 1 的点,若存在则递归调用它,以该点为基础继续向外扩散; 5 | 若四周均不存在值为 1 的点,或者已经到达边界,则结束该递归。 6 | 注:对于已经遍历过了的岛上的点,将其值置为2,防止重复计算。 7 | 8 | /* 9 | 给定一个二维数组,所有位置的值不是0就是1. 规定每个位置可以和它上下左右位置上的值相连。 10 | 有一个叫做岛的概念,定义如下: 11 | 连成一片的1,如果周围都是0,那么这一片1,构成一个岛。 12 | 求整张图上有多少个岛。 13 | 14 | 样例1: 15 | 输入: 16 | 0, 0, 0, 0, 0, 0, 0, 0, 0 17 | 0, 1, 1, 1, 0, 1, 1, 1, 0 18 | 0, 1, 1, 1, 0, 0, 0, 1, 0 19 | 0, 1, 1, 0, 0, 0, 0, 0, 0 20 | 0, 0, 0, 0, 0, 1, 1, 0, 0 21 | 0, 0, 0, 0, 1, 1, 1, 0, 0 22 | 0, 0, 0, 0, 0, 0, 0, 0, 0 23 | 输出: 24 | 3 25 | 26 | 样例2: 27 | 输入: 28 | 0, 0, 0, 0, 0, 0, 0, 0, 0 29 | 0, 1, 1, 1, 1, 1, 1, 1, 0 30 | 0, 1, 1, 1, 0, 0, 0, 1, 0 31 | 0, 1, 1, 0, 0, 0, 1, 1, 0 32 | 0, 0, 0, 0, 0, 1, 1, 0, 0 33 | 0, 0, 0, 0, 1, 1, 1, 0, 0 34 | 0, 0, 0, 0, 0, 0, 0, 0, 0 35 | 输出: 36 | 1 37 | */ 38 | 39 | public class Islands { 40 | // 遍历矩阵 41 | public static int countIslands(int[][] m) { 42 | if (m == null || m[0] == null) { 43 | return 0; 44 | } 45 | int N = m.length; 46 | int M = m[0].length; 47 | int res = 0; 48 | for (int i = 0; i < N; i++) { 49 | for (int j = 0; j < M; j++) { 50 | if (m[i][j] == 1) { 51 | res++; 52 | infect(m, i, j, N, M); 53 | } 54 | } 55 | } 56 | return res; 57 | } 58 | // 向外扩散函数,递归 59 | public static void infect(int[][] m, int i, int j, int N, int M) { 60 | // 若四周均不存在值为 1 的点,或者已经到达边界,则结束该递归。 61 | if (i < 0 || i >= N || j < 0 || j >= M || m[i][j] != 1) { 62 | return; 63 | } 64 | // 已经遍历过了的岛上的点将其值置为2,防止重复计算 65 | m[i][j] = 2; 66 | // 递归调用该函数向四周扩散 67 | infect(m, i + 1, j, N, M); 68 | infect(m, i - 1, j, N, M); 69 | infect(m, i, j + 1, N, M); 70 | infect(m, i, j - 1, N, M); 71 | } 72 | 73 | public static void main(String[] args) { 74 | int[][] m1 = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 75 | { 0, 1, 1, 1, 0, 1, 1, 1, 0 }, 76 | { 0, 1, 1, 1, 0, 0, 0, 1, 0 }, 77 | { 0, 1, 1, 0, 0, 0, 0, 0, 0 }, 78 | { 0, 0, 0, 0, 0, 1, 1, 0, 0 }, 79 | { 0, 0, 0, 0, 1, 1, 1, 0, 0 }, 80 | { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; 81 | System.out.println(countIslands(m1)); 82 | 83 | int[][] m2 = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 84 | { 0, 1, 1, 1, 1, 1, 1, 1, 0 }, 85 | { 0, 1, 1, 1, 0, 0, 0, 1, 0 }, 86 | { 0, 1, 1, 0, 0, 0, 1, 1, 0 }, 87 | { 0, 0, 0, 0, 0, 1, 1, 0, 0 }, 88 | { 0, 0, 0, 0, 1, 1, 1, 0, 0 }, 89 | { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, }; 90 | System.out.println(countIslands(m2)); 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /循环单词.java: -------------------------------------------------------------------------------- 1 | /* 2 | 如果一个单词通过循环右移获得的单词,我们称这些单词都为一种循环单词。 3 | 例如:picture 和 turepic 就是属于同一种循环单词。 4 | 现在给出n个单词,需要统计这个n个单词中有多少种循环单词。 5 | 6 | 输入描述: 7 | 输入包括n+1行: 8 | 第一行为单词个数n(1 ≤ n ≤ 50) 9 | 接下来的n行,每行一个单词word[i],长度length(1 ≤ length ≤ 50)。由小写字母构成 10 | 11 | 输出描述: 12 | 输出循环单词的种数 13 | 14 | 示例1 15 | 输入 16 | 5 17 | picture 18 | turepic 19 | icturep 20 | word 21 | ordw 22 | 23 | 输出 24 | 2 25 | */ 26 | 27 | /** 28 | * 这道题目的考点其实大家都非常熟悉: 29 | * 如何判断一个单词是否由另外一个单词旋转得来。(剑指offer中非常经典的一道题目) 30 | * 解法就是在原来的单词后面再次 append 原单词,比如:picture => picturepicture 31 | * 这样处理过后的单词就能够 contains 原单词的所有旋转情况。 32 | * 这也是本题中 循环单词 的定义。 33 | * 34 | * 那么知道了题目的核心考点之后,本题要求的是在给定的所有单词中有几种 循环单词? 35 | * 因此我们需要一个 容器 来存储当前遇到单词的 所有循环单词 情况。 36 | * 即每当遇到一个没有被 contains 的单词,我们就把该单词所有的 循环单词情况全部 add 到容器中。 37 | * 这里我们使用了 List, 当然 Set 也完全可以。 38 | */ 39 | import java.util.*; 40 | 41 | public class Main { 42 | public static void main(String args[]){ 43 | Scanner sc = new Scanner(System.in); 44 | int n = sc.nextInt(); 45 | 46 | List list = new ArrayList<>(); 47 | int count = 0; 48 | // 总共有 n 个单词 49 | for (int i = 0; i < n; i++) { 50 | String str = sc.next(); 51 | // 如果 List 中不能找到该单词,则说明该单词为新的一种 52 | if (!list.contains(str)) { 53 | count++; 54 | // 将该单词的 所有循环单词 全部加入到 List 中 55 | StringBuilder sb = new StringBuilder(str); 56 | sb.append(str); 57 | for (int j = 0; j < str.length(); j++) { 58 | list.add(sb.substring(j, j + str.length())); 59 | } 60 | } 61 | } 62 | 63 | System.out.println(count); 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /招银科技_整数成绩最大化.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:32768K 4 | 5 | 给出一个整数n,将n分解为至少两个整数之和,使得这些整数的乘积最大化,输出能够获得的最大的乘积。 6 | 例如: 7 | 2=1+1,输出1; 8 | 10=3+3+4,输出36。 9 | 10 | 输入描述: 11 | 输入为1个整数 12 | 13 | 输出描述: 14 | 输出为1个整数 15 | 16 | 输入例子1: 17 | 10 18 | 19 | 输出例子1: 20 | 36 21 | */ 22 | 23 | /** 24 | * Approach: Recursion + Memory Search 25 | * 看到这个第一反应一个类似完全背包问题的题目。 26 | * 因为使用 Memory Search 写起来非常简单并且易于理解,所以笔试中并没有采用 DP 的写法。 27 | * 记忆化搜索的做法很简单: 28 | * 使用 mem 来记录每个 n 所对应的最优解以避免重复计算。(这里可以使用数组代替) 29 | * 然后每次枚举所有的分割方案。(从 1~2/n,对于乘积的分割到 n/2 即可,再后面就重复了) 30 | * 在所有的方案中取最大值即可。 31 | * 注意:处理初始的几个数值即可(1,2,3) 32 | * 33 | * 事后看了下,DP写法基本没差,这里就不写了 O(∩_∩)O 34 | */ 35 | import java.util.*; 36 | 37 | public class Main { 38 | private static Map mem = new HashMap<>(); 39 | 40 | public static void main(String[] args) { 41 | Scanner sc = new Scanner(System.in); 42 | while (sc.hasNext()) { 43 | int n = sc.nextInt(); 44 | if (n == 2 || n == 3) { 45 | System.out.println(n - 1); 46 | continue; 47 | } else { 48 | System.out.println(dfs(n)); 49 | } 50 | } 51 | sc.close(); 52 | } 53 | 54 | private static long dfs(int n) { 55 | if (n == 1 || n == 2) { 56 | return n; 57 | } 58 | if (mem.containsKey(n)) { 59 | return mem.get(n); 60 | } 61 | 62 | long max = n; 63 | // Do DFS (1~n/2) 64 | for (int i = 1; i <= n / 2; i++) { 65 | max = Math.max(max, i * dfs(n - i)); 66 | } 67 | // Make a record 68 | mem.put(n, max); 69 | return max; 70 | } 71 | } 72 | 73 | /** 74 | * Approach 2: Mathematics 75 | * 该解法是网上看到的,跟大家分享一下。纯粹的找规律,数学题。 76 | * 77 | * 根据题目设定的条件整数n的取值范围为: 2 <= n <= 58 78 | * 分析一下: 79 | * 当 n = 2 时: n=1+1; result = 1*1=1 80 | * 当 n = 3 时:可以拆分为: 1+2 或者 1+1+1,但是显然拆分为 1+2,所获得的乘积最大 81 | * 当 n = 4 时:可以拆分为: 1+3 或者 2+2,但是显然拆分为 2+2,所获得的乘积最大 82 | * 当 n = 5 时:可以拆分为:2+3,所获得乘积最大 83 | * 当 n = 6 时:可以拆分为:3+3,所获得乘积最大 84 | * 当 n = 7 时:可以拆分为:3+4,所获得乘积最大 85 | * 当 n = 8 时:可以拆分为:3+3+2,所获得乘积最大 86 | * 当 n = 9 时:可以拆分为:3+3+3,所获得乘积最大 87 | * 当 n = 10 时:可以拆分为:3+3+4,所获得乘积最大 88 | * 通过观察上述内容,我们可以发现从n=5开始,拆分的结果都有数字3。 89 | * 之后的数字,例如11,可以先拆出来1个3,然后再看余下的8如何拆分。 90 | */ 91 | import java.util.*; 92 | 93 | public class Main { 94 | private static Map mem = new HashMap<>(); 95 | 96 | public static void main(String[] args) { 97 | Scanner sc = new Scanner(System.in); 98 | while (sc.hasNext()) { 99 | int n = sc.nextInt(); 100 | if (n == 2 || n == 3) { 101 | System.out.println(n - 1); 102 | continue; 103 | } 104 | if (n == 4) { 105 | System.out.println(4); 106 | continue; 107 | } 108 | int result = 1; 109 | while (n > 4) { 110 | result *= 3; 111 | n -= 3; 112 | } 113 | System.out.println(result * n); 114 | } 115 | sc.close(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /拼多多_迷宫寻路.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:131072K 4 | 5 | 假设一个探险家被困在了地底的迷宫之中,要从当前位置开始找到一条通往迷宫出口的路径。 6 | 迷宫可以用一个二维矩阵组成,有的部分是墙,有的部分是路。迷宫之中有的路上还有门, 7 | 每扇门都在迷宫的某个地方有与之匹配的钥匙,只有先拿到钥匙才能打开门。请设计一个算法,帮助探险家找到脱困的最短路径。 8 | 如前所述,迷宫是通过一个二维矩阵表示的,每个元素的值的含义如下 0-墙,1-路,2-探险家的起始位置, 9 | 3-迷宫的出口,大写字母-门,小写字母-对应大写字母所代表的门的钥匙 10 | 11 | 输入描述: 12 | 迷宫的地图,用二维矩阵表示。第一行是表示矩阵的行数和列数M和N 13 | 后面的M行是矩阵的数据,每一行对应与矩阵的一行(中间没有空格)。M和N都不超过100, 门不超过10扇。 14 | 15 | 输出描述: 16 | 路径的长度,是一个整数 17 | 18 | 示例1 19 | 输入 20 | 5 5 21 | 02111 22 | 01a0A 23 | 01003 24 | 01001 25 | 01111 26 | 输出 27 | 7 28 | */ 29 | 30 | /** 31 | * Approach: BFS 32 | * 和 LeetCode 上的 Shortest Path to Get All Keys 非常类似。 33 | * 利用 BFS 求最短路径即可,多了钥匙的信息,所以需要再开辟一维空间。 34 | * 因为迷宫的行列不会超过 100,钥匙不会多于 10 把。 35 | * 所以依然可以使用一个 整形 来表示所有状态。 36 | * 低10位表示钥匙状态;10~20位表示纵坐标;高位表示横坐标。 37 | * 38 | * 时间复杂度:O(m*n*2^1024) 39 | * 空间复杂度:O(m*n*2^1024) 40 | * 41 | * Shortest Path to Get All Keys: 42 | * https://github.com/cherryljr/LeetCode/blob/master/Shortest%20Path%20to%20Get%20All%20Keys.java 43 | */ 44 | 45 | import java.util.*; 46 | 47 | public class Main { 48 | public static void main(String[] args) { 49 | Scanner sc = new Scanner(System.in); 50 | while (sc.hasNext()) { 51 | int m = sc.nextInt(); 52 | int n = sc.nextInt(); 53 | char[][] maze = new char[m][n]; 54 | for (int i = 0; i < m; i++) { 55 | String str = sc.next(); 56 | for (int j = 0; j < n; j++) { 57 | maze[i][j] = str.charAt(j); 58 | } 59 | } 60 | System.out.println(BFS(maze)); 61 | } 62 | sc.close(); 63 | } 64 | 65 | private static int BFS(char[][] maze) { 66 | int rows = maze.length, cols = maze[0].length; 67 | Queue queue = new LinkedList<>(); 68 | boolean[][][] visited = new boolean[rows][cols][1024]; 69 | for (int i = 0; i < rows; i++) { 70 | for (int j = 0; j < cols; j++) { 71 | if (maze[i][j] == '2') { 72 | queue.offer((i << 20) | (j << 10)); 73 | visited[i][j][0] = true; 74 | break; 75 | } 76 | } 77 | } 78 | 79 | int step = 0; 80 | final int[][] DIRS = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; 81 | while (!queue.isEmpty()) { 82 | int size = queue.size(); 83 | for (int i = 0; i < size; i++) { 84 | int curr = queue.poll(); 85 | int row = curr >> 20 & 0x3ff, col = curr >> 10 & 0x3ff; 86 | int state = curr & 0x3ff; 87 | if (maze[row][col] == '3') { 88 | return step; 89 | } 90 | 91 | for (int[] dir : DIRS) { 92 | int nextRow = row + dir[0], nextCol = col + dir[1]; 93 | int nextState = state; 94 | if (nextRow < 0 || nextRow >= rows || nextCol < 0 || nextCol >= cols || maze[nextRow][nextCol] == '0') { 95 | continue; 96 | } 97 | if (Character.isUpperCase(maze[nextRow][nextCol]) && (state >> (maze[nextRow][nextCol] - 'A') & 1) == 0) { 98 | continue; 99 | } 100 | if (Character.isLowerCase(maze[nextRow][nextCol])) { 101 | nextState = state | (1 << maze[nextRow][nextCol] - 'a'); 102 | } 103 | if (!visited[nextRow][nextCol][nextState]) { 104 | queue.offer((nextRow << 20) | (nextCol << 10) | nextState); 105 | visited[nextRow][nextCol][nextState] = true; 106 | } 107 | } 108 | } 109 | step++; 110 | } 111 | 112 | return step; 113 | } 114 | 115 | } -------------------------------------------------------------------------------- /插入元素构造回文数组的最小代价.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述: 3 | 对于一个给定的正整数组成的数组a[], 如果将a倒序后数字的排列与a完全相同, 4 | 我们称这个数组为“回文”的。 5 | 例如:[1, 2, 3, 2, 1]的倒叙就是它自己,所以是一个回文数组; 6 | 而[1, 2, 3, 1, 2]的倒序是[2, 1, 3, 2, 1],所以不是一个回文的数组。 7 | 对于一个给定的正整数组成的数组,如果我们向其中某些特定的位置插入一些正整数, 8 | 那么我们总能构造出一个回文的数组。结果为 [1, 2, 1, 3, 1, 2, 1] 9 | 输入一个正整数组成的数组,要求你插入一些数字,使其变成回文的数组。 10 | 且数组中所有数字的和尽可能小。输出这个插入后数组中元素的和。 11 | 输入描述: 12 | 输入数据由两行组成:第一行包含一个正整数 L,表示数组 a 的长度。 13 | 第二行包含 L 个正整数,表述数组a。 14 | 输出描述: 15 | 输出一个整数,表示通过插入若干个正整数,使得数组 a 回文后, 16 | 数组 a 的数字和的最小值。 17 | */ 18 | 19 | /** 20 | * 该题与 删除最少的字符构成回文数组(实质为 LCS 问题)具有一定的相似性。 21 | * https://www.nowcoder.com/questionTerminal/28c1dc06bc9b4afd957b01acdf046e69 22 | * 23 | * 但是二者之间仍然有一定的区别,接下来我们来分析这两道题目。 24 | * (为了方便起见,删除最少的字符构成回文数组 该题将简称为“删除题”) 25 | * 这两道题目联系在于都可以通过 DP 来解决,区别在于: 26 | * "删除题"中,我们想要求的最终结果是在原本字符串基础上删除一部分得到的, 27 | * 即因为某一些不必要的字符串的插入,导致了原本是回文字符串的答案被分割成了题目给出的字符串。 28 | * 可到此很明显,那就是我们想要求的是原本字符串的 SubSequence。 29 | * 因此我们可以简单地通过 Reverse + LCS 的方法求得结果。 30 | * 31 | * 本题("插入题")与“删除题”的区别就在于,题目所给出的字符串中没有一个字符是多余的。 32 | * 它们都是最终结果的一部分。因此我们无法再通过 Reverse + LCS 的方法解决,因为它本身就是一个 SubSequence。 33 | * 但是我们仍然可以通过 DP 来解决该题。解法如下: 34 | * State: 35 | * dp[i][j] 表示 i~j 段需要插入数值的最小和。 36 | * 37 | * Function: 38 | * 当 nums[i] == nums[j] 时,本身即为回文数组,无需插入新数字。 39 | * dp[i][j] = dp[i+1][j-1]; 40 | * 当 nums[i] != nums[j] 时,插入二者中的最小值,构成回文数组。 41 | * dp[i][j] = Math.min(dp[i+1][j] + nums[i-1], dp[i][j-1] + nums[j-1]); 42 | * 43 | * Answer: 44 | * 原数组之和 sum + 插入数字之和的最小值 dp[1][n] 45 | * 46 | * PS. 这两题的解法都可以被应用于 Longest Palindrome Substring 中。 47 | * 但是都不是很好的解法。对于这道题目,有兴趣的可以看详细分析: 48 | * https://github.com/cherryljr/LintCode/blob/master/Longest%20Palindromic%20Substring.java 49 | * https://github.com/cherryljr/LintCode/blob/master/Manacher%20Template.java 50 | */ 51 | 52 | import java.util.*; 53 | 54 | public class Main { 55 | public static void main(String[] args) { 56 | Scanner sc = new Scanner(System.in); 57 | int n = sc.nextInt(); 58 | int[] nums = new int[n]; 59 | for (int i = 0; i < n; i++) { 60 | nums[i] = sc.nextInt(); 61 | } 62 | sc.close(); 63 | System.out.println(getAns(nums, n)); 64 | } 65 | 66 | public static int getAns(int[] nums, int n) { 67 | if (nums == null || nums.length <= 1) { 68 | return 0; 69 | } 70 | 71 | int sum = 0; 72 | for (int i = 0; i < n; i++) { 73 | sum += nums[i]; 74 | } 75 | sum += minInsert(nums, n); 76 | 77 | return sum; 78 | } 79 | 80 | public static int minInsert(int[] nums, int n) { 81 | int[][] dp = new int[n + 1][n + 1]; 82 | for (int i = n - 1; i >= 1; i--) { 83 | for (int j = i + 1; j <= n; j++) { 84 | if (nums[i - 1] == nums[j - 1]) { 85 | dp[i][j] = dp[i+1][j-1]; 86 | } else { 87 | dp[i][j] = Math.min(dp[i+1][j] + nums[i-1], dp[i][j-1] + nums[j-1]); 88 | } 89 | } 90 | } 91 | 92 | return dp[1][n]; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /操作序列.java: -------------------------------------------------------------------------------- 1 | /* 2 | 小易有一个长度为n的整数序列,a_1,...,a_n。然后考虑在一个空序列b上进行n次以下操作: 3 | 1、将a_i放入b序列的末尾 4 | 2、逆置b序列 5 | 小易需要你计算输出操作n次之后的b序列。 6 | 输入描述: 7 | 输入包括两行,第一行包括一个整数n(2 ≤ n ≤ 2*10^5),即序列的长度。 8 | 第二行包括n个整数a_i(1 ≤ a_i ≤ 10^9),即序列a中的每个整数,以空格分割。 9 | 10 | 输出描述: 11 | 在一行中输出操作n次之后的b序列,以空格分割,行末无空格。 12 | 13 | 示例1 14 | 输入 15 | 4 16 | 1 2 3 4 17 | 输出 18 | 4 2 1 3 19 | */ 20 | 21 | /** 22 | * 这实际上是一题找规律的题目,并不需要每次都进行逆序操作。 23 | * 其规律就是: 24 | * 最后结果的前一半由:原字符串从后向前间隔一个进行输出; 25 | * 后一半由:原字符串中未被输出的字符,从前往后按照原本顺序输出即可。 26 | * 27 | * 注意点: 28 | * 输出格式中,最后一个字符后面不能有空格。 29 | */ 30 | import java.util.Scanner; 31 | 32 | public class Main { 33 | public static void main(String[] args) { 34 | Scanner in = new Scanner(System.in); 35 | 36 | while (in.hasNextInt()) { 37 | int n = in.nextInt(); 38 | int[] nums = new int[n]; 39 | for (int i = 0; i < n; i++) { 40 | nums[i] = in.nextInt(); 41 | } 42 | // 前一半:从最后一个数开始以2为步长递减 43 | for (int i = n - 1; i >= 0; i -= 2) { 44 | System.out.print(nums[i] + " "); 45 | } 46 | // 后一半:根据整数个数的奇偶,分别从第二个或第一个数开始以2为步长递增 47 | for (int i = n % 2; i < n - 2; i += 2) { 48 | System.out.print(nums[i] + " "); 49 | } 50 | // 输出最后一个数 51 | System.out.print(nums[n - 2]); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /数组中各个元素出现的次数.java: -------------------------------------------------------------------------------- 1 | 该题的解法第一反应便是开辟额外 O(N) 空间的数组。 2 | 数组的 index 代表出现的元素,数组中储存的 value 代表该元素出现的次数。 3 | 这样遍历一遍数组便能够得到答案。 时间复杂度为: O(n) 4 | 5 | 但是该题要求不能开辟额外的空间,故我们只能考虑将该信息储存到原来的数组中。 6 | 但是将该信息放进原数组之后,原本的信息将被覆盖。因此我们必须先将原本的信息拿出来, 7 | 然后再放进与次数相关的信息。并且拿出来的信息(即数组中的元素)必须被马上使用。 8 | 不然堆积起来,我们又要开辟空间去储存它。因此我们可以将其当作数组的 index 去访问下一个元素。 9 | (因为index范围为:0~n-1,所以取出该元素后记得 -1) 10 | 但是我们如何区别我们访问到的数是代表着 出现的次数 还是 数组中原本的元素呢?(即是否被访问过) 11 | 如何让一个值体现这两种属性呢?我们知道一个数除了大小外还有正负这个属性, 12 | 因此这里便将被访问过的元素设为负值(因为原数组中元素均为正值且没被碰过), 13 | 于是第一次遇到某个元素时现将它清零后-1,以后每遇到一次就-1。最后将值取反便得到各元素对应的出现次数。 14 | 到这里我们已经想出来如何解决该题了。接下来只需要遍历一遍原数组即可。 15 | 时间复杂度为:O(n) 16 | 17 | /* 18 | 问题描述: 19 | 给定一个整数数组a,长度为N,元素取值范围为[1,N]。 20 | 统计各个元素出现的次数,要求时间复杂度为O(N),空间复杂度为O(1)。 21 | 22 | 示例: 23 | 输入: 24 | 7 25 | 1 4 1 4 2 1 0 26 | 输出: 27 | 1,3 2,1 3,0 4,2 5,0 6,0 7,0 28 | */ 29 | 30 | class Solution { 31 | public static void elementCounter(int[] nums, int n) { 32 | int index = 0; 33 | while (index < n) { 34 | int temp = nums[index] - 1; 35 | // 该元素已经访问过,代表的是出现次数,跳过 36 | if (temp < 0) { 37 | index++; 38 | continue; 39 | // 第一次遇到该值 40 | } else if (nums[temp] > 0) { 41 | // 将该元素暂存起来,并将次数置为 -1 42 | nums[index] = nums[temp]; 43 | nums[temp] = -1; 44 | // 不是第一次处理该值了 45 | } else { 46 | // 没有新元素需要处理,置0,并将次数 -1 47 | nums[index] = 0; 48 | nums[temp]--; 49 | } 50 | } 51 | 52 | for (int i = 0; i < n; i++) { 53 | System.out.print(i+1 + "," + -nums[i] + "\t"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /明明的随机数.java: -------------------------------------------------------------------------------- 1 | 题目要求:去重 + 排序 2 | 毫无疑问 TreeSet 该数据结构完美符合要求。直接使用该数据结构即可。 3 | 4 | /* 5 | 明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了N个1到1000之间的随机整数(N≤1000), 6 | 对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。 7 | 然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。 8 | 9 | Input Param 10 | n 输入随机数的个数 11 | inputArray n个随机整数组成的数组 12 | 13 | Return Value 14 | OutputArray 输出处理后的随机整数 15 | 16 | 注:测试用例保证输入参数的正确性,答题者无需验证。测试用例不止一组。 17 | */ 18 | 19 | import java.util.*; 20 | 21 | public class Main{ 22 | public static void main(String[] args) { 23 | Scanner sc = new Scanner(System.in); 24 | int n; 25 | while(sc.hasNext()) 26 | { 27 | n = sc.nextInt(); 28 | if (n > 0) { 29 | TreeSet ts = new TreeSet<>(); 30 | for (int i = 0; i < n; i++) { 31 | ts.add(sc.nextInt()); 32 | } 33 | for (int i : ts) { 34 | System.out.println(i); 35 | } 36 | } 37 | } 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /最多的会议场数.java: -------------------------------------------------------------------------------- 1 | /* 2 | 一些项目要占用一个会议室宣讲, 会议室不能同时容纳两个项目的宣讲。 3 | 给你每一个项目开始的时间和结束的时间(给你一个数组, 里面 是一个个具体的项目), 4 | 你来安排宣讲的日程, 要求会议室进行 的宣讲的场次最多。 5 | 返回这个最多的宣讲场次。 6 | 7 | 输入: 8 | n 代表有n个会议需要安排 9 | 之后的 n 行,每行有两个数子,代表会议的开始和结束的时间 10 | */ 11 | 12 | /** 13 | * Approach: Greedy 14 | * 这道题目与 Minimum Number of Arrows to Burst Balloons 非常地相似,用到同一个 贪心策略 15 | * https://github.com/cherryljr/LeetCode/blob/master/Minimum%20Number%20of%20Arrows%20to%20Burst%20Balloons.java 16 | * 17 | * 我们只需要按照会议结束的时间进行排序,然后依次安排,就能够安排最多的场数 18 | */ 19 | 20 | import java.io.BufferedInputStream; 21 | import java.util.Arrays; 22 | import java.util.Scanner; 23 | 24 | public class Main { 25 | static class Meeting { 26 | int start; 27 | int end; 28 | 29 | public Meeting(int start, int end) { 30 | this.start = start; 31 | this.end = end; 32 | } 33 | } 34 | 35 | public static void main(String[] args) { 36 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 37 | int n = sc.nextInt(); 38 | Meeting[] meetings = new Meeting[n]; 39 | int start, end; 40 | for (int i = 0; i < n; i++) { 41 | start = sc.nextInt(); 42 | end = sc.nextInt(); 43 | meetings[i] = new Meeting(start, end); 44 | } 45 | 46 | Arrays.sort(meetings, (a, b) -> a.end - b.end); 47 | int sum = 1; 48 | int endTime = meetings[0].end; 49 | for (int i = 1; i < meetings.length; i++) { 50 | if (meetings[i].start < endTime) { 51 | continue; 52 | } 53 | sum++; 54 | endTime = meetings[i].end; 55 | } 56 | 57 | System.out.println(sum); 58 | } 59 | } -------------------------------------------------------------------------------- /最大值减去最小值小于等于k的子数组数量.java: -------------------------------------------------------------------------------- 1 | /* 2 | 描述: 3 | 给定数组arr和整数num,共返回有多少个子数组满足如下情况: 4 | max(arr[i..j]) - min(arr[i..j]) <= num 5 | max(arr[i..j])表示子数组 arr[i..j] 的最大值; 6 | min(arr[i..j])表示子数组 arr[i..j] 中的最小值. 7 | 8 | 要求: 9 | 如果数组长度为 N,请实现时间复杂度为 O(N)的解法。 10 | */ 11 | 12 | /** 13 | * Approach: Deque 14 | * 如果采用暴力解法,我们需要遍历 所有大小 的窗口,然后再遍历该子数组是否符合条件。 15 | * 总时间复杂度为 O(n^3) 16 | * 17 | * 但是这道题目可以利用 双端队列 进行优化。 18 | * 对于一个子数组,设其最大值为 max, 最小值为 min. 19 | * 如果 符合条件(max-min <= num) 那么对于子该数组内所有的子数组,必定有 max'<=max, min'>=min. 20 | * 也就是其子数组必定符合条件。因此通过该子数组我们就能够推得: 21 | * 所有以 arr[left] 开头的能够组成的达标子数组总共有 right-left 个。 22 | * 如果 不符合条件(max-min <= num) 那么该子数组继续向右扩的话,必定不达标,因为这样只会导致 23 | * max 更大,或者 min 更小。 24 | * 25 | * 由上述分析可得: 26 | * 我们需要求得一个子数组(窗口)内的 最大值 和 最小值。 27 | * 因此我们可以借助两个 双端队列 来对时间复杂度进行优化。 28 | * 实现方法同 Sliding Window Maximum: 29 | * https://github.com/cherryljr/LeetCode/blob/master/Sliding%20Window%20Maximum.java 30 | * 具体实现: 31 | * 利用 left, right 表示窗口的左右边界 32 | * 一开始 right 向右扩,直到窗口内的子数组不达标为止。 33 | * 此时所有窗口内所有以 arr[left] 开头的能够组成的达标子数组总共有 right-left 个,则 rst += (right - left) 34 | * 然后因为 继续向右扩的话,窗口内数组无法达标,因此 左边界 向右移动(换一个开头)。 35 | * 这样因为 窗口中的元素被移除了一个,有可能导致窗口能够达标,因此继续以上操作,直到 左边界 到达数组末尾。 36 | * 37 | * 时间复杂度分析: 38 | * 左边界 与 右边界 一直向右移动,不会后退,因此时间复杂度为 O(n) 39 | */ 40 | public class Main { 41 | 42 | public static int getNum(int[] arr, int num) { 43 | if (arr == null || arr.length == 0) { 44 | return 0; 45 | } 46 | 47 | Deque qMin = new LinkedList<>(); // 采用双端队列来维持窗口的内的 最小值 48 | Deque qMax = new LinkedList<>(); // 采用双端队列来维持窗口的内的 最大值 49 | int left = 0; // 窗口左边界 50 | int right = 0; // 窗口右边界 51 | int rst = 0; 52 | while (left < arr.length) { 53 | // 左边界不动,右边界向右扩到不能继续扩时停止(子数组无法达标) 54 | while (right < arr.length) { 55 | // 当 arr[right] 大于等于 最大值双端队列尾部的值 且 队列不为空 时,将尾部元素弹出 56 | while (!qMax.isEmpty() && arr[qMax.getLast()] <= arr[right]) { 57 | qMax.removeLast(); 58 | } 59 | // 将该元素加入 最大双端队列的末尾 60 | qMax.addLast(right); 61 | // 当 arr[right] 小于等于 最小值双端队列尾部的值 且 队列不为空 时,将尾部元素弹出 62 | while (!qMin.isEmpty() && arr[qMin.getLast()] >= arr[right]) { 63 | qMin.removeLast(); 64 | } 65 | // 将该元素加入 最小双端队列的末尾 66 | qMin.addLast(right); 67 | // 当窗口中 最大值 与 最小值 的差值大于 num 时,right 停止移动 68 | if (arr[qMax.getFirst()] - arr[qMin.getFirst()] > num) { 69 | break; 70 | } 71 | right++; 72 | } 73 | 74 | // 当窗口 左边界值 过期时,将队列的 头部元素弹出 75 | if (qMax.getFirst() == left) { 76 | qMax.removeFirst(); 77 | } 78 | if (qMin.getFirst() == left) { 79 | qMin.removeFirst(); 80 | } 81 | // 窗口内的 子数组 均符合条件 82 | rst += right - left; 83 | // 左边界 向右移动,换一个开头,看是否能够使得窗口内的数组达标 84 | left++; 85 | } 86 | 87 | return rst; 88 | } 89 | 90 | public static void main(String[] args) { 91 | int[] arr = getRandomArray(30); 92 | int num = 5; 93 | printArray(arr); 94 | System.out.println(getNum(arr, num)); 95 | } 96 | 97 | //for test 98 | public static int[] getRandomArray(int len) { 99 | if (len < 0) { 100 | return null; 101 | } 102 | 103 | int[] arr = new int[len]; 104 | for (int i = 0; i < len; i++) { 105 | arr[i] = (int) (Math.random() * 10); 106 | } 107 | return arr; 108 | } 109 | 110 | //for test 111 | public static void printArray(int[] arr) { 112 | if (arr != null) { 113 | for (int i = 0; i < arr.length; i++) { 114 | System.out.print(arr[i] + " "); 115 | } 116 | System.out.println(); 117 | } 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /最大子矩阵和.java: -------------------------------------------------------------------------------- 1 | Max Sum of Rectangle No Larger Than K 的一个子问题。 2 | 使用到了 动态规划 和 Kadane's algorithm. 3 | 主要的思想为: 4 | 遍历所有的行数组,将遍历的所有行数组相加,然后产生一个新的数组, 5 | 将问题转换为:Maximum Subarray. 6 | 然后只要求出所有 Maximum Subarray 的最大值即可。 7 | 8 | /* 9 | 给定一个矩阵 matrix,其中矩阵中的元素可以包含正数、负数、和0,返回子矩阵的最大累加和。 10 | 例如,矩阵 matrix 为: 11 | 0 -2 -7 0 12 | 9 2 -6 2 13 | -4 1 -4 1 14 | -1 8 0 -2 15 | 16 | 拥有最大和的子矩阵为: 17 | 9 2 18 | -4 1 19 | -1 8 20 | 其和为15。返回15 21 | */ 22 | 23 | public class Main { 24 | public static void main(String[] args) { 25 | // 最大子矩阵的累加和 26 | int matrix[][] = {{0, -2, -7, 0}, {9, 2, -6, 2}, {-4, 1, -4, 1}, {-1, 8, 0, -2}}; 27 | maxSubmatrix(matrix); 28 | } 29 | 30 | public static void maxSubmatrix(int matrix[][]) { 31 | if(matrix == null || matrix.length == 0) { 32 | return; 33 | } 34 | 35 | int max = 0; 36 | int col = matrix[0].length; 37 | int row = matrix.length; 38 | for (int i = 0; i < row; i++) { 39 | int arr[] = new int[col]; 40 | for (int j = i; j < row; j++) { 41 | //遍历所有的子行 42 | for(int k = 0; k < col; k++) { 43 | arr[k] += matrix[j][k]; 44 | //将每子行的值进行相加然后利用子数组的最大和就可以求出子矩阵的最大和 45 | } 46 | max = Math.max(maxSubArray(arr), max); 47 | // 求出数组的子数组和最大值 48 | } 49 | } 50 | System.out.println(max); 51 | } 52 | // Kadane's algorithm 53 | // 还有另外一种 Prefix Sum 的写法 54 | public static int maxSubArray(int[] nums) { 55 | if (nums == null || nums.length == 0) { 56 | return 0; 57 | } 58 | 59 | int sum = 0; 60 | int max = Integer.MIN_VALUE; 61 | for (int i : nums) { 62 | sum += i; 63 | max = Math.max(max, sum); 64 | sum = Math.max(sum, 0); 65 | } 66 | 67 | return max; 68 | } 69 | } -------------------------------------------------------------------------------- /最小数字乘以区间和的最大值.java: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个数组序列, 需要求选出一个区间, 使得该区间是所有区间中经过如下计算的值最大的一个: 3 | 区间中的最小数 * 区间所有数的和最后程序输出经过计算后的最大值即可,不需要输出具体的区间。 4 | 如给定序列  [6 2 1]则根据上述公式, 可得到所有可以选定各个区间的计算值: 5 | [6] = 6 * 6 = 36; 6 | [2] = 2 * 2 = 4; 7 | [1] = 1 * 1 = 1; 8 | [6,2] = 2 * 8 = 16; 9 | [2,1] = 1 * 3 = 3; 10 | [6, 2, 1] = 1 * 9 = 9; 11 | 从上述计算可见选定区间 [6] ,计算值为 36, 则程序输出为 36。 12 | 区间内的所有数字都在[0, 100]的范围内; 13 | 14 | 输入描述: 15 | 第一行输入数组序列长度n,第二行输入数组序列。 16 | 对于 50%的数据, 1 <= n <= 10000; 17 | 对于 100%的数据, 1 <= n <= 500000; 18 | 19 | 输出描述: 20 | 输出数组经过计算后的最大值。 21 | 示例1 22 | 输入 23 | 3 24 | 6 2 1 25 | 输出 26 | 36 27 | */ 28 | 29 | /** 30 | * Approach 1: Brute Force 31 | * 直接对数组进行大小为[i, j]的窗口遍历。 32 | * 同时计算区间和 sum; 更新区间最小值 min; 最后由 sum * min 得到结果。 33 | * 看是否比 max 大,如果更大,更新 max 即可。 34 | * 属于相当暴力直观的方法。 35 | * 时间复杂度为:O(n^2) 36 | */ 37 | import java.util.*; 38 | 39 | public class Main { 40 | public static void main(String args[]){ 41 | Scanner sc = new Scanner(System.in); 42 | int size = sc.nextInt(); 43 | int[] input = new int[size]; 44 | for (int i = 0; i < input.length; i++) { 45 | input[i] = sc.nextInt(); 46 | } 47 | 48 | long max = input[0] * input[0]; 49 | for (int i = 0; i < size; i++) { 50 | long sum = input[i]; 51 | int min = input[i]; 52 | for (int j = i+1; j < size; j++) { 53 | // 如果值为 0 直接退出即可 54 | if (input[j] == 0) { 55 | break; 56 | } 57 | sum += input[j]; 58 | if (input[j] < min) { 59 | min = input[j]; 60 | } 61 | if (min * sum > max) { 62 | max = min * sum; 63 | } 64 | } 65 | } 66 | 67 | System.out.println(max); 68 | } 69 | } 70 | 71 | /** 72 | * Approach 2: 利用单调栈寻找左右边界 73 | * 把每个数字都看作是当前区间内的最小值,那么只要区间和的值越大,结果值就越大。 74 | * 75 | * 因此我们可以利用(递增)单调栈得到每个元素的左边界和右边界 76 | * (边界的定义即为 左/右边 第一个比该元素更小的值),最后用每个元素乘以每个元素对应的区间和,找出最大值即可。 77 | * 78 | * 为了防止每个元素重复计算一段区间和,可以提前计算一个前缀和数组,用于保存某元素之前的各项和(不含该元素), 79 | * 求取一段区间和的时候用右边界的前缀和减去左边界的前缀和即可。 80 | * 81 | * 这个方法的优点在于只需要遍历数组一次,大大降低了时间复杂度. 82 | * 时间复杂度为:O(2n). 各个元素进出栈各一次。 83 | */ 84 | import java.util.*; 85 | 86 | public class Main { 87 | public static void main(String[] args) { 88 | Scanner sc = new Scanner(System.in); 89 | int len = sc.nextInt(); 90 | int[] nums = new int[len]; 91 | for (int i = 0; i < len; i++) { 92 | nums[i] = sc.nextInt(); 93 | } 94 | 95 | System.out.println(getMaxIntervalSum(nums)); 96 | } 97 | 98 | public static long getMaxIntervalSum(int[] nums) { 99 | long max = 0; 100 | long[] preSum = new long[nums.length + 1]; 101 | for (int i = 1; i <= nums.length; i++) { 102 | preSum[i] = preSum[i - 1] + nums[i - 1]; 103 | } 104 | 105 | Stack stack = new Stack<>(); 106 | for (int i = 0; i <= nums.length; i++) { 107 | int curr = i == nums.length ? -1 : nums[i]; 108 | while (!stack.isEmpty() && curr < nums[stack.peek()]) { 109 | int num = nums[stack.pop()]; 110 | if (stack.isEmpty()) { 111 | max = Math.max(max, preSum[i] * num); 112 | } else { 113 | max = Math.max(max, (preSum[i] - preSum[stack.peek() + 1]) * num); 114 | } 115 | } 116 | stack.push(i); 117 | } 118 | 119 | return max; 120 | } 121 | } 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /最小编辑代价.java: -------------------------------------------------------------------------------- 1 | /* 2 | 对于两个字符串A和B,我们需要进行插入、删除和修改操作将A串变为B串,定义c0,c1,c2分别为三种操作的代价. 3 | 请设计一个高效算法,求出将A串变为B串所需要的最少代价。 4 | 给定两个字符串A和B,及它们的长度和三种操作代价,请返回将A串变为B串所需要的最小代价。 5 | 保证两串长度均小于等于300,且三种代价值均小于等于100。 6 | 7 | 测试样例: 8 | "abc",3,"adc",3,5,3,100 9 | 返回:8 10 | */ 11 | 12 | /** 13 | * 经典的动态规划题型 14 | * State: 15 | * 建立一个二维数组 dp[A.length() + 1][B.length() + 1]. 16 | * dp[i][j]表示将 A 中前i个字符转成 B 中前j个字符所需要的最小代价。 17 | * 18 | * Initiailize: 19 | * dp[0][i]:当 A 为空串,转换成 B 需要的代价为:c_insert * i; 20 | * dp[i][0]: 当 B 为空串,转换成 B 需要的代价为: c_delete * i; 21 | * 22 | * Function: 23 | * 计算出 dp[i][j] 总共有 4 中情况: 24 | * 1. 先将 A[0...i-1] 编辑成 A[0...i-2],也就是删除字符 A[i-1],然后由 A[0...i-2] 编辑成 B[0...j-1]. 25 | * dp[i-1][j] 就表示将 A[0...i-2] 编辑成 B[0...j-1] 的最小代价,即可能等于 dp[i-1][j] + c_delete. 26 | * 2. 先将 A[0...i-1] 编辑成 B[0...j-2],然后将 B[0...j-2] 插入字符 B[j-1] 编辑成 B[0...j-1]. 27 | * 即相当于在 A[0...i-1] 中插入 B[j-1]. 28 | * dp[i][j-1] 就表示将 A[0...i-1] 编辑成 B[0...j-2] 的最小代价,即可能等于 dp[i][j-1] + c_insert. 29 | * 3. 如果 A[i-1] != B[j-1]. 先把 A[0...i-2] 的部分变成 B[0...j-2],然后把字符 A[i-1] 替换成 B[j-1], 30 | * 这样就把 A[0...i-1] 编辑成了 B[0...j-1]. 31 | * dp[i-1][j-1] 表示将 A[0...i-2] 编辑成了 B[0...j-2] 的最小代价,即可能等于 dp[i-1][j-1] + c_change. 32 | * 4. 如果 A[i-1] == B[j-1]. 先把 A[0...i-2] 的部分变成 B[0...j-2],又因为此时 A[i-1] 已经等于 B[j-1]. 33 | * dp[i-1][j-1] 表示将 A[0...i-2] 编辑成了 B[0...j-2] 的最小代价,即可能等于 dp[i-1][j-1]. 34 | * 以上四种情况取最小值作为 dp[i][j] 的结果. 35 | * 36 | * Answer: 37 | * dp[A.length()][B.length()] 38 | */ 39 | 40 | import java.util.*; 41 | 42 | public class MinCost { 43 | public int findMinCost(String A, int n, String B, int m, int c_insert, int c_delete, int c_change) { 44 | if (A == null || A.length() == 0) { 45 | return c_insert * B.length(); 46 | } 47 | if (B == null || B.length() == 0) { 48 | return c_delete * A.length(); 49 | } 50 | 51 | // Initialize the dp array 52 | int[][] dp = new int[A.length() + 1][B.length() + 1]; 53 | // 初始化矩阵第一列,代表将字符串A中所有的字符全部删掉 54 | for (int i = 0; i <= A.length(); i++) { 55 | dp[i][0] = c_delete * i; 56 | } 57 | // 初始化矩阵第一行,代表在空串A中插入i个字符 58 | for (int i = 0; i <= B.length(); i++) { 59 | dp[0][i] = c_insert * i; 60 | } 61 | 62 | // Function 63 | for (int i = 1; i <= A.length(); i++) { 64 | for (int j = 1; j <= B.length(); j++) { 65 | if (A.charAt(i - 1) == B.charAt(j - 1)) { 66 | dp[i][j] = dp[i-1][j-1]; 67 | } else { 68 | dp[i][j] = Math.min(dp[i-1][j-1] + c_change, Math.min(dp[i-1][j] + c_delete, dp[i][j-1] + c_insert)); 69 | } 70 | } 71 | } 72 | 73 | return dp[A.length()][B.length()]; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /最长01子串.java: -------------------------------------------------------------------------------- 1 | /* 2 | 给定一个数组,数组中只包含0和1。请找到一个最长的子序列,其中0和1的数量是相同的。 3 | 例1:10101010 结果就是其本身。 4 | 例2:1101000 结果是110100 5 | */ 6 | 7 | /** 8 | * Approach: Prefix Sum + HashMap 9 | * 首先我们要对原来的数组进行变换一下。原来是 0 和 1 的串,我们将 0 都换为-1。 10 | * 则我们要找的子串是 最长的和为0 的子串。 11 | * 这种子串求和的问题,一般采用前缀和的方法来解决。 12 | * 用 preSum[i]代表 前i个 数的和,问题的模型转换为: 13 | * 找到i和j,满足 preSum[i] 与 preSum[j]相等,且|i-j|最大。 14 | * 然后使用 HashMap 作为辅助数据结构,map中的 key 为 preSum 的值,value 为 preSum[i] 第一次出现的位置。 15 | * 从左到右遍历 preSum[],看在 map 中查找是否存在, 16 | * 如果存在,则记录下 map.get(preSum[i]) 和 i 的距离差, 17 | * 否则说明该值第一次出现,将其 put 到 map 中即可。 18 | * 一次遍历结束后得到最大的距离差,同时也可以得到具体是哪一段。 19 | * 20 | * 类似的题目还有:“和最大的子数组”,和为0的子数组”,“和最接近0的子数组”。 21 | * 其通用的解决方法都是通过 前缀和 来解决。 22 | * https://github.com/cherryljr/LintCode/blob/master/Maximum%20Subarray.java 23 | */ 24 | public class Main { 25 | public static void main(String[] args) { 26 | System.out.println(maxSubString("1101100")); 27 | } 28 | 29 | public static String maxSubString(String str) { 30 | if (str == null || str.length() == 0) { 31 | return null; 32 | } 33 | 34 | int len = str.length(); 35 | int[] preSum = new int[len + 1]; 36 | char[] arr = str.toCharArray(); 37 | // Get the Prefix Sum 38 | for (int i = 1; i <= len; i++) { 39 | preSum[i] = arr[i - 1] == '1' ? 1 : -1; 40 | preSum[i] += preSum[i - 1]; 41 | } 42 | 43 | Map map = new HashMap<>(); 44 | int start = 0, maxLen = 0; 45 | // Using HashMap to get the maxLen 46 | for (int i = 0; i <= len; i++) { 47 | if (!map.containsKey(preSum[i])) { 48 | map.put(preSum[i], i); 49 | continue; 50 | } 51 | if (i - map.get(preSum[i]) > maxLen) { 52 | maxLen = i - map.get(preSum[i]); 53 | start = map.get(preSum[i]); 54 | } 55 | } 56 | 57 | return str.substring(start, start + maxLen); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /有趣的数字.java: -------------------------------------------------------------------------------- 1 | /* 2 | 小Q今天在上厕所时想到了这个问题:有n个数,两两组成二元组,差最小的有多少对呢?差最大呢? 3 | 4 | 输入描述: 5 | 输入包含多组测试数据。 6 | 对于每组测试数据: 7 | N - 本组测试数据有n个数 8 | a1,a2...an - 需要计算的数据 9 | 保证: 10 | 1<=N<=100000,0<=ai<=INT_MAX. 11 | 12 | 输出描述: 13 | 对于每组数据,输出两个数,第一个数表示差最小的对数,第二个数表示差最大的对数。 14 | 15 | 示例1 16 | 输入 17 | 6 18 | 45 12 45 32 5 6 19 | 输出 20 | 1 2 21 | */ 22 | 23 | /** 24 | * Approach: Sort + HashMap 25 | * 比较简单的题目。通常遇到求 最大/最小差值 的情况,我们都会利用到排序。 26 | * 因此首先我们相对整个数组进行排序即可。 27 | * 然后 最大差值 必定在数组的 头部元素 和 尾部元素 之间产生。 28 | * 而 最小差值 必定在两个 相邻元素 之间产生。 29 | * 又因为数组内可能包含 重复元素。 30 | * 这给我们统计的时候带来了一点点麻烦,所以我们需要利用 HashMap 来统计 31 | * 各个元素,以及其对应的出现次数,接下来只需要计算即可。 32 | * 33 | * 注意: 34 | * 1. 排序后可以检查一下数组的 头尾元素是否相等,如果相等说明数组内元素均相同,直接利用 35 | * C(n, 2) 计算出差值对数即可。 36 | * 2. 题目包含 多组测试数据,因此请务必使用 while(sc.hasNext()). 37 | */ 38 | 39 | import java.util.*; 40 | 41 | public class Main { 42 | 43 | public static void main(String[] args) { 44 | Scanner sc = new Scanner(System.in); 45 | while (sc.hasNext()) { 46 | int n = sc.nextInt(); 47 | int[] arr = new int[n]; 48 | for (int i = 0; i < n; i++) { 49 | arr[i] = sc.nextInt(); 50 | } 51 | 52 | // 先对数组进行排序以方便统计 53 | Arrays.sort(arr); 54 | // 如果数组中的值全相同,直接两两组合 C(n, 2) 55 | if (arr[0] == arr[n - 1]) { 56 | int rst = (n * (n - 1)) / 2; 57 | System.out.println(rst + " " + rst); 58 | continue; 59 | } 60 | // 利用 map 来统计各个数出现的次数 61 | Map map = new HashMap<>(); 62 | for (int i = 0; i < n; i++) { 63 | map.put(arr[i], map.getOrDefault(arr[i], 0) + 1); 64 | } 65 | 66 | // 求差最小的对数 67 | int minCount = 0; 68 | if (map.size() == n) { 69 | // 如果 map的大小 等于 数组大小,说明没有重复元素 70 | // 则遍历一遍数组,计算相邻元素的差值,取最小即可 71 | int min = Integer.MAX_VALUE; 72 | for (int i = 1; i < n; i++) { 73 | int temp = arr[i] - arr[i - 1]; 74 | if (temp < min) { 75 | min = temp; 76 | minCount = 1; 77 | } else if (temp == min) { 78 | minCount++; 79 | } 80 | } 81 | } else { 82 | // 若 map的大小 小于 数组大小,说明包含重复元素,即 min=0, 83 | // 则直接计算由重复元素组成的对数即可(可能存在多组重复元素) 84 | for (Map.Entry entry : map.entrySet()) { 85 | int value = entry.getValue(); 86 | if (value > 1) { 87 | minCount += (value * (value - 1) >> 1); 88 | } 89 | } 90 | } 91 | 92 | // 求差最大的对数 93 | // 直接取出 最小值 和 最大值 的出现次数相乘即可 94 | int maxCount = 0; 95 | int val1 = map.get(arr[0]); 96 | int val2 = map.get(arr[n - 1]); 97 | maxCount = val1 * val2; 98 | System.out.println(minCount + " " + maxCount); 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /树的子结构.java: -------------------------------------------------------------------------------- 1 | /* 2 | 输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) 3 | */ 4 | 5 | /** 6 | * Approach: Recursion 7 | * 与 Subtree of Another Tree 十分类似 8 | * https://github.com/cherryljr/LeetCode/blob/master/Subtree%20of%20Another%20Tree.java 9 | * 10 | * 区别在于本题求的是:子结构而不是子树,因此匹配条件更加宽松了。 11 | * 即只需要被包含即可。因此 Tree2 已经全部被遍历完后,Tree1 的子节点可以不为空。 12 | */ 13 | 14 | /** 15 | * public class TreeNode { 16 | * int val = 0; 17 | * TreeNode left = null; 18 | * TreeNode right = null; 19 | * public TreeNode(int val) { 20 | * this.val = val; 21 | * } 22 | * } 23 | */ 24 | public class Solution { 25 | public boolean HasSubtree(TreeNode root1,TreeNode root2) { 26 | // 当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false 27 | if (root1 == null || root2 == null) { 28 | return false; 29 | } 30 | 31 | // 如果两棵树完全相同,或者 Tree2 在 Tree1 的左子树或者右子树中,均返回 true 32 | if (isSameTree(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2)) { 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | private boolean isSameTree(TreeNode node1, TreeNode node2) { 39 | // 如果Tree2已经遍历完了都能对应的上,返回true 40 | if (node2 == null) { 41 | return true; 42 | } 43 | // 如果Tree2还没有遍历完,Tree1却遍历完了。返回false 44 | if (node1 == null) { 45 | return false; 46 | } 47 | // 如果当前节点没有对应上,返回false 48 | if (node1.val != node2.val) { 49 | return false; 50 | } 51 | // 如果当前节点对应的上,那么就分别去子节点里面匹配 52 | return isSameTree(node1.left, node2.left) && isSameTree(node1.right, node2.right); 53 | } 54 | } -------------------------------------------------------------------------------- /求出现次数的TopK问题.java: -------------------------------------------------------------------------------- 1 | 先遍历一遍整个数组进行词频统计,用 HashMap 储存每个 String 出现的次数。 2 | 维护一个 K 大小的最小堆,遍历 HashMap,当堆内元素未满K个时,直接将该元素 3 | offer 到堆中,当堆满了之后,每次将遍历到元素的 timers 与 堆顶元素的出现次数相比, 4 | 若更大,这将堆顶元素弹出推入新的元素。重新进行堆的构造。 5 | 毫无以为,这里我们会用到 PriorityQueue 这个数据结构。 6 | 该数据结构的特点是:每当 offer(), poll() 一个元素的时候,都会对堆进行重新构造, 7 | 并且保证堆顶为最小元素。 8 | 9 | /* 10 | 题目: 11 | 给定String类型的数组strArr,再给定整数k,请严格按照排名顺序打印出现次数前k名的字符串。 12 | 若出现次数一样多可以任意打印两个字符串。 13 | 14 | 示例1: 15 | strArr = {"1", "2", "3", "4"} k = 2 16 | 输出: 17 | No1: 1, times: 1 18 | No2: 2, times: 1 19 | 20 | 示例2: 21 | strArr = {"1", "1", "3", "4"} k = 2 22 | 输出: 23 | No1: 1, times: 2 24 | No2: 3, times: 1 25 | 26 | 要求: 27 | 时间复杂度为 O(nlogk) 28 | n为 strArr 的长度 29 | 30 | */ 31 | 32 | import java.util.ArrayList; 33 | import java.util.Comparator; 34 | import java.util.HashMap; 35 | import java.util.Map.Entry; 36 | import java.util.PriorityQueue; 37 | 38 | public class Solution { 39 | 40 | public static class Node { 41 | public String str; 42 | public int times; 43 | 44 | public Node(String s, int t) { 45 | str = s; 46 | times = t; 47 | } 48 | } 49 | 50 | public static void printTopKAndRank(String[] arr, int topK) { 51 | if (arr == null || topK < 1) { 52 | return; 53 | } 54 | 55 | // Use HashMap to restore the occurences of a string / number 56 | HashMap map = new HashMap(); 57 | for (int i = 0; i != arr.length; i++) { 58 | String cur = arr[i]; 59 | if (!map.containsKey(cur)) { 60 | map.put(cur, 1); 61 | } else { 62 | map.put(cur, map.get(cur) + 1); 63 | } 64 | } 65 | 66 | // Use Heap to restore topK strings 67 | PriorityQueue pq = new PriorityQueue<>(topK, new Comparator(){ 68 | @Override 69 | public int compare(Node a, Node b) { 70 | return a.times - b.times; 71 | } 72 | }); 73 | int index = 0; 74 | for (Entry entry : map.entrySet()) { 75 | String str = entry.getKey(); 76 | int times = entry.getValue(); 77 | Node node = new Node(str, times); 78 | if (index != topK) { 79 | pq.offer(node); 80 | index++; 81 | } else { 82 | // peek element has min occurences in topK element 83 | if (pq.peek().times < node.times) { 84 | pq.poll(); 85 | pq.offer(node); 86 | } 87 | } 88 | } 89 | 90 | // Reverse the element in Heap 91 | int len = pq.size(); 92 | ArrayList rst = new ArrayList<>(); 93 | for (int i = 0; i < len; i++) { 94 | // Once poll an element the heap will heapify again. 95 | rst.add(pq.poll()); 96 | } 97 | 98 | int count = 0; 99 | for (int i = rst.size() - 1; i >= 0; i--) { 100 | System.out.print("No." + (++count) + ": "); 101 | System.out.print(rst.get(i).str + ", times: "); 102 | System.out.println(rst.get(i).times); 103 | } 104 | } 105 | 106 | public static String[] generateRandomArray(int len, int max) { 107 | String[] res = new String[len]; 108 | for (int i = 0; i != len; i++) { 109 | res[i] = String.valueOf((int) (Math.random() * (max + 1))); 110 | } 111 | return res; 112 | } 113 | 114 | public static void printArray(String[] arr) { 115 | for (int i = 0; i != arr.length; i++) { 116 | System.out.print(arr[i] + " "); 117 | } 118 | System.out.println(); 119 | } 120 | 121 | public static void main(String[] args) { 122 | String[] arr = generateRandomArray(50, 10); 123 | int topK = 5; 124 | printArray(arr); 125 | printTopKAndRank(arr, topK); 126 | 127 | } 128 | } -------------------------------------------------------------------------------- /汽水瓶.java: -------------------------------------------------------------------------------- 1 | 解法1: 2 | 通过数学分析,最后获得的饮料数是总空瓶数整除2 。 3 | 解法2: 4 | 普通解法,通过简单的递归调用解决。 5 | 6 | /* 7 | 有这样一道智力题:“某商店规定:三个空汽水瓶可以换一瓶汽水。 8 | 小张手上有十个空汽水瓶,她最多可以换多少瓶汽水喝?”答案是5瓶. 9 | 方法如下:先用9个空瓶子换3瓶汽水,喝掉3瓶满的,喝完以后4个空瓶子,用3个再换一瓶,喝掉这瓶满的, 10 | 这时候剩2个空瓶子。然后你让老板先借给你一瓶汽水,喝掉这瓶满的,喝完以后用3个空瓶子换一瓶满的还给老板。 11 | 如果小张手上有n个空汽水瓶,最多可以换多少瓶汽水喝? 12 | */ 13 | 14 | // Soluton1: Math Analysis 15 | import java.util.*; 16 | 17 | public class Main{ 18 | public static void main(String[] args){ 19 | Scanner sc = new Scanner(System.in); 20 | int n; 21 | while(sc.hasNext()) 22 | { 23 | n = sc.nextInt(); 24 | System.out.println(Drink(n)); 25 | } 26 | 27 | } 28 | 29 | public static int Drink(int n) 30 | { 31 | if(n <= 0) { 32 | return 0; 33 | } 34 | else { 35 | return n / 2; 36 | } 37 | } 38 | } 39 | 40 | // Solution 2: Recursion 41 | import java.util.*; 42 | 43 | public class Main{ 44 |     public static void main(String[] args){ 45 |         Scanner sc = new Scanner(System.in); 46 |         int n; 47 |         while(sc.hasNext()) 48 |         { 49 |             n = sc.nextInt(); 50 |             System.out.println(Drink(n)); 51 |         } 52 |           53 |     } 54 |       55 |     public static int Drink(int n) 56 |     { 57 |         if(n <= 0) { 58 | return 0; 59 | } 60 |         else if (n == 3) { 61 |             return 1; 62 | } 63 |         else if (n == 2) { 64 |             return 1; 65 | } 66 |         else // 此时表明对应为 3 的倍数,递归 67 |         { 68 |             int h = 0; 69 |             h = n / 3; 70 |             return h + Drink(n - 3 * h + h); 71 |         } 72 |               73 |           74 |     } 75 | } -------------------------------------------------------------------------------- /滴滴_餐馆.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 某餐馆有n张桌子,每张桌子有一个参数:a 可容纳的最大人数; 4 | 有m批客人,每批客人有两个参数:b人数,c预计消费金额。 5 | 在不允许拼桌的情况下,请实现一个算法选择其中一部分客人,使得总预计消费金额最大 6 | 7 | 输入描述: 8 | 输入包括m+2行。 第一行两个整数n(1 <= n <= 50000),m(1 <= m <= 50000) 第二行为n个参数a,即每个桌子可容纳的最大人数,以空格分隔,范围均在32位int范围内。 接下来m行,每行两个参数b,c。分别表示第i批客人的人数和预计消费金额,以空格分隔,范围均在32位int范围内。 9 | 输出描述: 10 | 输出一个整数,表示最大的总预计消费金额 11 | 12 | 示例1 13 | 输入 14 | 3 5 2 4 2 1 3 3 5 3 7 5 9 1 10 15 | 输出 16 | 20 17 | */ 18 | 19 | /** 20 | * Approach: Greedy 21 | * 这道题目实际上是一个贪心的做法。 22 | * 策略就是:我们优先接纳那些消费最多的客户。(和现实生活一样呢...) 23 | * 为了实现贪心策略,我们需要对 桌子(从小到大) 和 客户(按金额从大到小) 进行排序。 24 | * 这里使用到了 优先级队列(最大堆) 25 | * 具体解释详见注释。 26 | */ 27 | 28 | import java.io.BufferedInputStream; 29 | import java.util.*; 30 | 31 | public class Main { 32 | public static void main(String[] args) { 33 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 34 | int n = sc.nextInt(), m = sc.nextInt(); 35 | int[] desks = new int[n]; 36 | boolean[] visited = new boolean[n]; 37 | for (int i = 0; i < n; i++) { 38 | desks[i] = sc.nextInt(); 39 | } 40 | // 餐桌按照 从小到大 进行排序 41 | Arrays.sort(desks); 42 | // 客人按照 消费金额 从大到小 排序 43 | PriorityQueue maxHeap = new PriorityQueue<>(); 44 | for (int i = 0; i < m; i++) { 45 | int people = sc.nextInt(), cost = sc.nextInt(); 46 | if (people <= desks[n - 1]) { 47 | maxHeap.offer(new Consumer(people, cost)); 48 | } 49 | } 50 | 51 | // 不开long long见祖宗,十年OI一场空 52 | long maxProfit = 0; 53 | while (!maxHeap.isEmpty()) { 54 | Consumer curr = maxHeap.poll(); 55 | // 因为桌子已经按大小排序好了, 56 | // 所以在 desks 中二分查找第一个 大于等于 curr.people 的桌子(能容纳下的桌子) 57 | int index = binarySearch(curr.people, desks); 58 | // 因为 二分查找 到的是第一个位置,但是 desks 数组中是存在 重复 元素的。 59 | // 所以我们无法确定后面的桌子是否被使用过, 60 | // 如果当前桌子被使用,我们就遍历后面的元素找到里 index 最近的未被使用的桌子 61 | while (index < n && visited[index]) { 62 | index++; 63 | } 64 | // 如果存在能够容纳人数并且还未被使用的桌子,就说明我们能够接待该批客人 65 | if (index < n) { 66 | visited[index] = true; 67 | maxProfit += curr.cost; 68 | } 69 | } 70 | System.out.println(maxProfit); 71 | } 72 | 73 | private static int binarySearch(int people, int[] desks) { 74 | int left = 0, right = desks.length; 75 | while (left < right) { 76 | int mid = left + ((right - left) >> 1); 77 | if (people <= desks[mid]) { 78 | right = mid; 79 | } else { 80 | left = mid + 1; 81 | } 82 | } 83 | return left; 84 | } 85 | 86 | static class Consumer implements Comparable { 87 | int people, cost; 88 | 89 | public Consumer(int people, int cost) { 90 | this.people = people; 91 | this.cost = cost; 92 | } 93 | 94 | @Override 95 | public int compareTo(Consumer other) { 96 | return other.cost - this.cost; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /牛牛打响指.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒空间限制:32768K 3 | 4 | 牛牛在地上捡到了一个手套,他带上手套发现眼前出现了很多个小人,当他打一下响指,这些小人的数量就会发生以下变化: 5 | 如果小人原本的数量是偶数那么数量就会变成一半,如果小人原本的数量是奇数那么数量就会加一。 6 | 现在牛牛想考考你,他要打多少次响指,才能让小人的数量变成1。 7 | 8 | 输入描述: 9 | 每个输入包含一个测试用例。 10 | 输入的第一行包括一个正整数,表示一开始小人的数量N(1<=N<=10^100)。 11 | 12 | 输出描述: 13 | 对于每个用例,在单独的一行中输出牛牛需要打多少次响指才能让小人的数量变成1。 14 | 示例1 15 | 输入 16 | 10000 17 | 输出 18 | 20 19 | */ 20 | 21 | /** 22 | * Approach: Simulation 23 | */ 24 | import java.io.BufferedReader; 25 | import java.io.InputStreamReader; 26 | import java.math.BigDecimal; 27 | import java.math.BigInteger; 28 | import java.util.*; 29 | 30 | public class Main { 31 | public static void main(String[] args) { 32 | InputReader input = new InputReader(); 33 | long num = input.nextLong(); 34 | 35 | long count = 0; 36 | while (num != 1) { 37 | if ((num & 1) == 0) { 38 | num >>>= 1; 39 | count++; 40 | } else { 41 | num += 1; 42 | count++; 43 | } 44 | } 45 | 46 | System.out.println(count); 47 | } 48 | 49 | static class InputReader { 50 | BufferedReader buffer; 51 | StringTokenizer token; 52 | 53 | InputReader() { 54 | buffer = new BufferedReader(new InputStreamReader(System.in)); 55 | } 56 | 57 | boolean hasNext() { 58 | while (token == null || !token.hasMoreElements()) { 59 | try { 60 | token = new StringTokenizer(buffer.readLine()); 61 | } catch (Exception e) { 62 | return false; 63 | } 64 | } 65 | return true; 66 | } 67 | 68 | String next() { 69 | if (hasNext()) return token.nextToken(); 70 | return null; 71 | } 72 | 73 | int nextInt() { 74 | return Integer.parseInt(next()); 75 | } 76 | 77 | long nextLong() { 78 | return Long.parseLong(next()); 79 | } 80 | 81 | double nextDouble() { 82 | return Double.parseDouble(next()); 83 | } 84 | 85 | BigInteger nextBigInteger() { 86 | return new BigInteger(next()); 87 | } 88 | 89 | BigDecimal nextBigDecimal() { 90 | return new BigDecimal(next()); 91 | } 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /牛牛的数列.java: -------------------------------------------------------------------------------- 1 | /* 2 | 牛牛现在有一个n个数组成的数列,牛牛现在想取一个连续的子序列,并且这个子序列还必须得满足: 3 | 最多只改变一个数,就可以使得这个连续的子序列是一个严格上升的子序列,牛牛想知道这个连续子序列最长的长度是多少。 4 | 5 | 输入描述: 6 | 输入包括两行,第一行包括一个整数n(1 ≤ n ≤ 10^5),即数列的长度; 7 | 第二行n个整数a_i, 表示数列中的每个数(1 ≤ a_i ≤ 10^9),以空格分割。 8 | 9 | 输出描述: 10 | 输出一个整数,表示最长的长度。 11 | 12 | 示例1 13 | 输入 14 | 6 15 | 7 2 3 1 5 6 16 | 17 | 输出 18 | 5 19 | */ 20 | 21 | /** 22 | * Approach: DP 23 | * 该题是在 Longest Increasing Continuous Subsequence 的基础上改编过来的。 24 | * https://github.com/cherryljr/LintCode/blob/master/Longest%20Increasing%20Continuous%20Subsequence.java 25 | * 因此题目的主要核心是相同的,我们只需要加入一些条件判断便能够 AC 这道题目。 26 | * 27 | * 首先我们需要有两个 DP 数组分别用于储存 从左往右 和 从右往左 的 LICS. 28 | * 它们分别为: 29 | * dpLeft[i], 它表示以 nums[i] 作为结尾的最长递增子串的长度; 30 | * dpRight[i], 它表示以 nums[i] 作为起始的最长递增子串的长度. 31 | * 因为我们可以修改一个位置的值,因此: 32 | * 当 num[i - 1] 与 nums[i + 1] 之间至少相差 2 时,我们可以通过修改 nums[i] 使得 nums[i-1] 和 nums[i+1] 也组成连续递增序列。 33 | * 值为: dpLeft[i - 1] + dpRight[i + 1] + 1 34 | * 若不成立,我们取 dpLeft[i - 1] 和 dpRight[i + 1] 中的较大值,然后修改 nums[i]. 组成一个 LICS. 35 | * 值为: Math.max(dpLeft[i - 1], dpRight[i + 1]) + 1 36 | */ 37 | import java.util.*; 38 | 39 | public class Main { 40 | public static void main(String args[]){ 41 | Scanner sc = new Scanner(System.in); 42 | int len = sc.nextInt(); 43 | int[] nums = new int[len]; 44 | for (int i = 0; i < len; i++) { 45 | nums[i] = sc.nextInt(); 46 | } 47 | 48 | // 记录正序遍历得到的各个递增子序列长度 49 | int[] dpLeft = new int[len]; 50 | // 记录逆序遍历得到的各个递增子序列长度 51 | int[] dpRight = new int[len]; 52 | 53 | // 正向统计连续递增序列的长度(以第i位数结尾的递增子序列) 54 | dpLeft[0] = 1; 55 | for (int i = 1; i < len; i++) { 56 | dpLeft[i] = nums[i - 1] < nums[i] ? dpLeft[i - 1] + 1 : 1; 57 | } 58 | 59 | // 逆向统计连续递增序列的长度(以第i位数开始的递增子序列) 60 | dpRight[len - 1] = 1; 61 | for (int i = len - 2; i >= 0; i--) { 62 | dpRight[i] = nums[i] < nums[i + 1] ? dpRight[i + 1] + 1 : 1; 63 | } 64 | 65 | // 最小的序列长度为1,所以把 rst 初始化为1 66 | int rst = 1; 67 | for (int i = 1; i < len - 1; i++) { 68 | // 对于每一位置i有左侧到它的最长连续递增序列dpLeft[i], 右侧有连续递增子序列长度dpRight[i] 69 | // 加1是因为 nums[i] 可进行修改从而组成 LICS 的一部分, 因此长度要算上第i位数。 70 | rst = Math.max(rst, Math.max(dpLeft[i - 1], dpRight[i + 1]) + 1); 71 | if (nums[i + 1] - nums[i - 1] >= 2) { 72 | // 第i+1位 与 第i-1位 至少相差2,则可以修改第i位数,使第i-1、i、i+1也可以组成连续递增序列。 73 | rst = Math.max(rst, dpLeft[i - 1] + dpRight[i + 1] + 1); 74 | } 75 | } 76 | 77 | System.out.println(rst); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /牛牛的背包问题.java: -------------------------------------------------------------------------------- 1 | /* 2 | 牛牛准备参加学校组织的春游, 出发前牛牛准备往背包里装入一些零食, 牛牛的背包容量为w。 3 | 牛牛家里一共有n袋零食, 第i袋零食体积为v[i]。 4 | 牛牛想知道在总体积不超过背包容量的情况下,他一共有多少种零食放法(总体积为0也算一种放法)。 5 | 6 | 输入描述: 7 | 输入包括两行 8 | 第一行为两个正整数n和w(1 <= n <= 30, 1 <= w <= 2 * 10^9),表示零食的数量和背包的容量。 9 | 第二行n个正整数v[i](0 <= v[i] <= 10^9),表示每袋零食的体积。 10 | 11 | 输出描述: 12 | 输出一个正整数, 表示牛牛一共有多少种零食放法。 13 | 14 | 示例1 15 | 输入 16 | 3 10 17 | 1 2 4 18 | 输出 19 | 8 20 | 21 | 说明 22 | 三种零食总体积小于10,于是每种零食有放入和不放入两种情况,一共有2*2*2 = 8种情况。 23 | */ 24 | 25 | /** 26 | * Approach: Binary Enumeration + Binary Search 27 | * 根据数据量可以推测出应该采用折半枚举 + 二分查找的方法。 28 | * 与 High Capacity Backpack 基本相同,只不过这里求的是方法数罢了 29 | * 相对的会更加简单一些。 30 | * 31 | * High Capacity Backpack: 32 | * https://github.com/cherryljr/LintCode/blob/master/High%20Capacity%20Backpack.java 33 | */ 34 | 35 | import java.util.Arrays; 36 | import java.util.Scanner; 37 | 38 | public class Main { 39 | public static void main(String[] args) { 40 | Scanner sc = new Scanner(System.in); 41 | while (sc.hasNext()) { 42 | int n = sc.nextInt(); 43 | int w = sc.nextInt(); 44 | int[] snacks = new int[n]; 45 | for (int i = 0; i < n; i++) { 46 | snacks[i] = sc.nextInt(); 47 | } 48 | 49 | int half = n >> 1; 50 | long[] records = new long[1 << half]; 51 | for (int i = 0; i < 1 << half; i++) { 52 | long sum = 0; 53 | for (int j = 0; j < half; j++) { 54 | if ((i >> j & 1) == 1) { 55 | sum += snacks[j]; 56 | } 57 | } 58 | records[i] = sum; 59 | } 60 | Arrays.sort(records); 61 | 62 | int left = n - half; 63 | int rst = 0; 64 | for (int i = 0; i < 1 << left; i++) { 65 | long sum = 0; 66 | for (int j = 0; j < left; j++) { 67 | if ((i >> j & 1) == 1) { 68 | sum += snacks[half + j]; 69 | } 70 | } 71 | rst += upperBound(records, w - sum); 72 | } 73 | System.out.println(rst); 74 | } 75 | } 76 | 77 | private static int upperBound(long[] records, long target) { 78 | int left = -1, right = records.length - 1; 79 | while (left < right) { 80 | int mid = left + (right - left + 1 >> 1); 81 | if (target >= records[mid]) { 82 | left = mid; 83 | } else { 84 | right = mid - 1; 85 | } 86 | } 87 | return left + 1; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /筛法求素数.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/筛法求素数.java -------------------------------------------------------------------------------- /纸牌博弈.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/纸牌博弈.java -------------------------------------------------------------------------------- /网易-骰子游戏.java: -------------------------------------------------------------------------------- 1 | /* 2 | 小易参加了一个骰子游戏,这个游戏需要同时投掷n个骰子,每个骰子都是一个印有数字1~6的均匀正方体。 3 | 小易同时投掷出这n个骰子,如果这n个骰子向上面的数字之和大于等于x,小易就会获得游戏奖励。 4 | 小易想让你帮他算算他获得奖励的概率有多大。 5 | 6 | 输入描述: 7 | 输入包括两个正整数n和x(1 ≤ n < 25, 1 ≤ x < 150),分别表示骰子的个数和可以获得奖励的最小数字和。 8 | 输出描述: 9 | 输出小易可以获得奖励的概率。 10 | 如果概率为1,输出1,如果概率为0,输出0,其他以最简分数(x/y)的形式输出。 11 | 12 | 示例1 13 | 输入 14 | 3 9 15 | 输出 16 | 20/27 17 | */ 18 | 19 | /** 20 | * Approach 1: DP (01-Backpack) 21 | * 这个问题实际上就是一个 01背包问题。 22 | * 即求使用 n 个数,组成 m 的方案个数。 23 | * 24 | * n个骰子的和最大为6n 最小为n 25 | * 用 dp[n][m] 表示投 前n个骰子 时点数之和为 m 的个数 26 | * 投第n个骰子的点数之和只与第n-1个骰子有关 27 | * dp(n,m)=dp(n-1,m-1)+dp(n-1,m-2)+dp(n-1,m-2)+dp(n-1,m-3)+dp(n-1,m-4)+dp(n-1,m-5)+dp(n-1,m-6) 28 | * 表示本轮点数之和为n的次数等于上一轮点数和为n-1,n-2,n-3,n-4,n-5,n-6出现的次数之和 29 | * 例如: 30 | * dp(2,6)=dp(1,5)+dp(1,4)+dp(1,3)+dp(1,2)+dp(1,1)+dp(1,0)=1+1+1+1+1=5 31 | * dp(3,6)=dp(2,5)+dp(2,4)+dp(2,3)+dp(2,2)+dp(2,1)+dp(2,0) 32 | * 33 | * 时间复杂度:O(n^2) 34 | * 空间复杂度:O(n^2) 35 | */ 36 | import java.util.Scanner; 37 | 38 | public class Main { 39 | public static void main(String[] args) { 40 | Scanner sc = new Scanner(System.in); 41 | int dices = sc.nextInt(), num = sc.nextInt(); 42 | if (num <= dices) { 43 | System.out.println(1); 44 | return; 45 | } else if (num > 6 * dices) { 46 | System.out.println(0); 47 | return; 48 | } 49 | 50 | // 获取投掷 n 个骰子所能组成的sum值的分布数组(出现次数) 51 | long[] counts = getCounts(dices); 52 | // n个骰子向上面的数字之和大于等于x的情况数总和 53 | long res = 0L; 54 | for (int i = num; i <= 6 * dices; i++) { 55 | res += counts[i]; 56 | } 57 | // 所有组合个数 58 | long all = (long)Math.pow(6, dices); 59 | // 最大公约数 60 | long divisor = gcd(res, all); 61 | 62 | System.out.println(res / divisor + "/" + all / divisor); 63 | } 64 | 65 | private static long gcd(long a, long b) { 66 | return b == 0 ? a : gcd(b, a % b); 67 | } 68 | 69 | private static long[] getCounts(int dices) { 70 | long[][] dp = new long[dices + 1][6 * dices + 1]; 71 | for (int i = 1; i <= 6; i++) { 72 | dp[1][i] = 1; 73 | } 74 | for (int i = 2; i <= dices; i++) { 75 | for (int j = i; j <= 6 * i; j++) { 76 | for (int k = 1; k <= 6 && j > k; k++) { 77 | dp[i][j] += dp[i - 1][j - k]; 78 | } 79 | } 80 | } 81 | return dp[dices]; 82 | } 83 | } 84 | 85 | 86 | /** 87 | * Approach: 0-1 Backpack (Space Optimized) 88 | * 时间复杂度:O(n^2) 89 | * 空间复杂度:O(n) 90 | */ 91 | import java.util.Scanner; 92 | 93 | public class Main { 94 | public static void main(String[] args) { 95 | Scanner sc = new Scanner(System.in); 96 | int dices = sc.nextInt(), num = sc.nextInt(); 97 | if (num <= dices) { 98 | System.out.println(1); 99 | return; 100 | } else if (num > 6 * dices) { 101 | System.out.println(0); 102 | return; 103 | } 104 | 105 | // 获取投掷 n 个骰子所能组成的sum值的分布数组(出现次数) 106 | long[] counts = getCounts(dices); 107 | // n个骰子向上面的数字之和大于等于x的情况数总和 108 | long res = 0L; 109 | for (int i = num; i <= 6 * dices; i++) { 110 | res += counts[i]; 111 | } 112 | // 所有组合个数 113 | long all = (long)Math.pow(6, dices); 114 | // 最大公约数 115 | long divisor = gcd(res, all); 116 | 117 | System.out.println(res / divisor + "/" + all / divisor); 118 | } 119 | 120 | private static long gcd(long a, long b) { 121 | return b == 0 ? a : gcd(b, a % b); 122 | } 123 | 124 | private static long[] getCounts(int dices) { 125 | long[] dp = new long[6 * dices + 1]; 126 | for (int i = 1; i <= 6; i++) { 127 | dp[i] = 1; 128 | } 129 | for (int i = 1; i < dices; i++) { 130 | for (int j = dp.length - 1; j > 0; j--) { 131 | dp[j] = 0; 132 | for (int k = 1; k <= 6 && j > k; k++) { 133 | dp[j] += dp[j - k]; 134 | } 135 | } 136 | } 137 | return dp; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /网易_回文序列.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 如果一个数字序列逆置之后跟原序列是一样的就称这样的数字序列为回文序列。 4 | 例如: 5 | {1, 2, 1}, {15, 78, 78, 15} , {112} 是回文序列, 6 | {1, 2, 2}, {15, 78, 87, 51} ,{112, 2, 11} 不是回文序列。 7 | 现在给出一个数字序列,允许使用一种转换操作: 8 | 选择任意两个相邻的数,然后从序列移除这两个数,并用这两个数字的和插入到这两个数之前的位置(只插入一个和)。 9 | 现在对于所给序列要求出最少需要多少次操作可以将其变成回文序列。 10 | 11 | 输入描述: 12 | 输入为两行,第一行为序列长度n ( 1 ≤ n ≤ 50) 第二行为序列中的n个整数item[i] (1 ≤ iteam[i] ≤ 1000),以空格分隔。 13 | 14 | 输出描述: 15 | 输出一个数,表示最少需要的转换次数 16 | 17 | 示例1 18 | 输入 19 | 4 20 | 1 1 1 3 21 | 输出 22 | 2 23 | */ 24 | 25 | /** 26 | * Approach: Deque | Two Pointers 27 | * 本题可以使用双端队列deque数据结构进行求解。 28 | * 主要思想为:判断队首和队尾元素。 29 | * 若二者相等,则将这两个元素都弹出队列,将队列规模缩小2个,再对该问题进行判断; 30 | * 若二者不相等,则选择其中较小的一个,将该元素和与其相邻的元素都弹出队列, 31 | * 再将其和插入队列,从而将队列规模缩小1个,再对该问题进行判断。 32 | * 33 | * 但实际上,我们也可以不使用双端队列,直接使用数据进行操作,维护两个指针 head, tail 34 | * 也能够达到同样的效果。 35 | * 36 | * 时间复杂度:O(n) 37 | * 空间复杂度:O(1) 38 | */ 39 | 40 | import java.io.BufferedReader; 41 | import java.io.InputStreamReader; 42 | import java.math.BigDecimal; 43 | import java.math.BigInteger; 44 | import java.util.*; 45 | 46 | public class Main { 47 | public static void main(String[] args) { 48 | InputReader input = new InputReader(); 49 | while (input.hasNext()) { 50 | int n = input.nextInt(); 51 | int[] nums = new int[n]; 52 | for (int i = 0; i < n; i++) { 53 | nums[i] = input.nextInt(); 54 | } 55 | 56 | int head = 0, tail = n - 1; 57 | int times = 0; 58 | while (head < tail) { 59 | if (nums[head] > nums[tail]) { 60 | nums[--tail] += nums[tail + 1]; 61 | times++; 62 | } else if (nums[head] < nums[tail]) { 63 | nums[++head] += nums[head - 1]; 64 | times++; 65 | } else { 66 | head++; 67 | tail--; 68 | } 69 | } 70 | System.out.println(times); 71 | } 72 | } 73 | 74 | // 输入输出模板 75 | static class InputReader { 76 | BufferedReader buffer; 77 | StringTokenizer token; 78 | 79 | InputReader() { 80 | buffer = new BufferedReader(new InputStreamReader(System.in)); 81 | } 82 | 83 | boolean hasNext() { 84 | while (token == null || !token.hasMoreElements()) { 85 | try { 86 | token = new StringTokenizer(buffer.readLine()); 87 | } catch (Exception e) { 88 | return false; 89 | } 90 | } 91 | return true; 92 | } 93 | 94 | String next() { 95 | if (hasNext()) return token.nextToken(); 96 | return null; 97 | } 98 | 99 | int nextInt() { 100 | return Integer.parseInt(next()); 101 | } 102 | 103 | long nextLong() { 104 | return Long.parseLong(next()); 105 | } 106 | 107 | double nextDouble() { 108 | return Double.parseDouble(next()); 109 | } 110 | 111 | BigInteger nextBigInteger() { 112 | return new BigInteger(next()); 113 | } 114 | 115 | BigDecimal nextBigDecimal() { 116 | return new BigDecimal(next()); 117 | } 118 | } 119 | 120 | } -------------------------------------------------------------------------------- /网易_字符串编码.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:32768K 4 | 5 | 给定一个字符串,请你将字符串重新编码,将连续的字符替换成“连续出现的个数+字符”。 6 | 比如字符串AAAABCCDAA会被编码成4A1B2C1D2A。 7 | 8 | 输入描述: 9 | 每个测试输入包含1个测试用例 10 | 每个测试用例输入只有一行字符串,字符串只包括大写英文字母,长度不超过10000。 11 | 12 | 输出描述: 13 | 输出编码后的字符串 14 | 15 | 输入例子1: 16 | AAAABCCDAA 17 | 18 | 输出例子1: 19 | 4A1B2C1D2A 20 | */ 21 | 22 | 23 | /** 24 | * Approach: Traverse 25 | * 热身题...遍历往后走就行 26 | */ 27 | import java.util.Scanner; 28 | 29 | public class Main { 30 | public static void main(String[] args) { 31 | Scanner sc = new Scanner(System.in); 32 | while (sc.hasNext()) { 33 | String str = sc.next(); 34 | StringBuilder sb = new StringBuilder(); 35 | int index = 0; 36 | while (index < str.length()) { 37 | int count = 1; 38 | while (index < str.length() - 1 && str.charAt(index) == str.charAt(index + 1)) { 39 | count++; 40 | index++; 41 | } 42 | sb.append(count).append(str.charAt(index++)); 43 | } 44 | System.out.println(sb.toString()); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /网易_安置路灯.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:32768K 4 | 5 | 小Q正在给一条长度为n的道路设计路灯安置方案。 6 | 为了让问题更简单,小Q把道路视为n个方格,需要照亮的地方用'.'表示, 不需要照亮的障碍物格子用'X'表示。 7 | 小Q现在要在道路上设置一些路灯, 对于安置在pos位置的路灯, 这盏路灯可以照亮pos - 1, pos, pos + 1这三个位置。 8 | 小Q希望能安置尽量少的路灯照亮所有'.'区域, 希望你能帮他计算一下最少需要多少盏路灯。 9 | 10 | 输入描述: 11 | 输入的第一行包含一个正整数t(1 <= t <= 1000), 表示测试用例数 12 | 接下来每两行一个测试数据, 第一行一个正整数n(1 <= n <= 1000),表示道路的长度。 13 | 第二行一个字符串s表示道路的构造,只包含'.'和'X'。 14 | 15 | 输出描述: 16 | 对于每个测试用例, 输出一个正整数表示最少需要多少盏路灯。 17 | 18 | 输入例子1: 19 | 2 20 | 3 21 | .X. 22 | 11 23 | ...XX....XX 24 | 25 | 输出例子1: 26 | 1 27 | 3 28 | */ 29 | 30 | /** 31 | * Approach: Greedy 32 | * 不知道为什么网易这么喜欢路灯呢... 33 | * 之前可考过这么一道题目,而且还十分相似...都是对 贪心 的考察 34 | * https://github.com/cherryljr/NowCoder/blob/master/%E7%BD%91%E6%98%93_%E8%B7%AF%E7%81%AF.java 35 | * 36 | * 没什么好说的了,看到之后第一反应就是 贪心. 37 | * 因为非常明显,我们需要每个灯都能尽可能地覆盖 最大范围。 38 | * 那么对于 "..." 这三个位置而言,肯定是放在 第二个 位置上是最优的。 39 | * 因此我们可以按照如上的策略,遍历整个字符串: 40 | * 遇到 障碍 时不用管,直接跳过即刻。 41 | * 遇到 需要照亮的位置 时,我们直接向后跳 3 个位置(路灯能照亮的距离) 42 | * 43 | * 时间复杂度为:O(n) 44 | */ 45 | 46 | import java.io.BufferedInputStream; 47 | import java.util.*; 48 | 49 | public class Main { 50 | public static void main(String[] args) { 51 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 52 | for (int T = sc.nextInt(); T-- > 0;) { 53 | int n = sc.nextInt(); 54 | int rst = 0; 55 | String str = sc.next(); 56 | int pos = 0; 57 | while (pos < str.length()) { 58 | // 遇到障碍 59 | if (str.charAt(pos) == 'X') { 60 | pos++; 61 | continue; 62 | } 63 | // 遇到需要照亮的位置 64 | if (str.charAt(pos) == '.') { 65 | rst++; 66 | pos += 3; 67 | } 68 | } 69 | System.out.println(rst); 70 | } 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /网易_幸运的袋子.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 一个袋子里面有n个球,每个球上面都有一个号码(拥有相同号码的球是无区别的)。 4 | 如果一个袋子是幸运的当且仅当所有球的号码的和大于所有球的号码的积。 5 | 例如:如果袋子里面的球的号码是{1, 1, 2, 3},这个袋子就是幸运的,因为1 + 1 + 2 + 3 > 1 * 1 * 2 * 3 6 | 你可以适当从袋子里移除一些球(可以移除0个,但是别移除完),要使移除后的袋子是幸运的。 7 | 现在让你编程计算一下你可以获得的多少种不同的幸运的袋子。 8 | 9 | 输入描述: 10 | 第一行输入一个正整数n(n ≤ 1000) 11 | 第二行为n个数正整数xi(xi ≤ 1000) 12 | 输出描述: 13 | 输出可以产生的幸运的袋子数 14 | 15 | 示例1 16 | 输入 17 | 3 18 | 1 1 1 19 | 输出 20 | 2 21 | */ 22 | 23 | 24 | /** 25 | * Approach: DFS 26 | * 直接枚举所有的 Subsets 方案,然后判断其是否能够组成幸运袋即可。 27 | * 值得注意的是:在枚举的过程中,我们需要进行剪枝操作, 28 | * 不然会进行大量无用的计算,从而导致超时。 29 | * 因为 DFS 的时间复杂还是相当高的,所以普遍的做法就是: 30 | * 在 DFS 之前首先进行一个 排序 操作,然后可以利用 有序 的特性对 DFS 过程进行剪枝优化。 31 | * 因为 nums 已经排序好了,所以当 sum < product 的时候,后面可以直接略去不再进行计算。 32 | * 而对于 去除重复的subsets同样是利用了实现 排序 的做法。 33 | * 去重的详细分析可以参考: 34 | * Subsets II: https://github.com/cherryljr/LintCode/blob/master/Subsets%20II.java 35 | * 36 | * 排序不仅能帮助我们去重,还能帮助我们进行剪枝。(因此通常我们都会先进行一次排序操作) 37 | * 类似的应用还有 38 | * Combination Sum: https://github.com/cherryljr/LeetCode/blob/master/Combination%20Sum.java 39 | */ 40 | import java.io.BufferedInputStream; 41 | import java.util.*; 42 | 43 | public class Main { 44 | private static int result = 0; 45 | public static void main(String[] args) { 46 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 47 | int n = sc.nextInt(); 48 | int[] nums = new int[n]; 49 | for (int i = 0; i < n; i++) { 50 | nums[i] = sc.nextInt(); 51 | } 52 | 53 | Arrays.sort(nums); 54 | dfs(nums, 0, 0, 1); 55 | System.out.println(result); 56 | } 57 | 58 | private static void dfs(int[] nums, int index, int sum, int product) { 59 | if (sum > product) { 60 | result++; 61 | } 62 | 63 | for (int i = index; i < nums.length; i++) { 64 | // 剪枝 65 | if (sum + nums[i] < product * nums[i]) { 66 | break; 67 | } 68 | if (i > 0 && i != index && nums[i] == nums[i - 1]) { 69 | continue; 70 | } 71 | dfs(nums, i + 1, sum + nums[i], product * nums[i]); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /网易_数字游戏.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 小易邀请你玩一个数字游戏,小易给你一系列的整数。你们俩使用这些整数玩游戏。 4 | 每次小易会任意说一个数字出来,然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字。 5 | 例如: 如果{2,1,2,7}是你有的一系列数,小易说的数字是11.你可以得到方案2+2+7 = 11. 6 | 如果顽皮的小易想坑你,他说的数字是6,那么你没有办法拼凑出和为6。 7 | 现在小易给你n个数,让你找出无法从n个数中选取部分求和的数字中的最小数。 8 | 9 | 输入描述: 10 | 输入第一行为数字个数n (n ≤ 20) 11 | 第二行为n个数xi (1 ≤ xi ≤ 100000) 12 | 13 | 输出描述: 14 | 输出最小不能由n个数选取求和组成的数 15 | 16 | 示例1 17 | 输入 18 | 3 19 | 5 1 2 20 | 输出 21 | 4 22 | */ 23 | 24 | /** 25 | * Approach: Similar to Backpack 26 | * 基于这样一种迭代的思想: 27 | * 假如说可以得到最大的数为 k,则再来一个新的数字p, 28 | * 如果 p<=k+1,则我可以凑出的最大的数为 p+k, 29 | * 如果 p>k+1,则会出现空挡,我们肯定无法凑出 k+1 。 30 | * 31 | * 本题实际上是Google的一道考题的简化版: 32 | * https://code.google.com/codejam/contest/4244486/dashboard#s=p2&a=2 33 | */ 34 | 35 | import java.io.BufferedInputStream; 36 | import java.util.Arrays; 37 | import java.util.Scanner; 38 | 39 | public class Main { 40 | public static void main(String[] args) { 41 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 42 | int n = sc.nextInt(); 43 | int[] nums = new int[n]; 44 | for (int i = 0; i < n; i++) { 45 | nums[i] = sc.nextInt(); 46 | } 47 | 48 | Arrays.sort(nums); 49 | int min = 0; 50 | for (int i = 0; i < n; i++) { 51 | if (nums[i] > min + 1) { 52 | break; 53 | } 54 | min += nums[i]; 55 | } 56 | System.out.println(min + 1); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /网易_数对.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:32768K 4 | 5 | 牛牛以前在老师那里得到了一个正整数数对(x, y), 牛牛忘记他们具体是多少了。 6 | 但是牛牛记得老师告诉过他x和y均不大于n, 并且x除以y的余数大于等于k。 7 | 牛牛希望你能帮他计算一共有多少个可能的数对。 8 | 9 | 输入描述: 10 | 输入包括两个正整数n,k(1 <= n <= 10^5, 0 <= k <= n - 1)。 11 | 12 | 输出描述: 13 | 对于每个测试用例, 输出一个正整数表示可能的数对数量。 14 | 15 | 输入例子1: 16 | 5 2 17 | 18 | 输出例子1: 19 | 7 20 | 21 | 例子说明1: 22 | 满足条件的数对有(2,3),(2,4),(2,5),(3,4),(3,5),(4,5),(5,3) 23 | */ 24 | 25 | /** 26 | * Approach: Mathmatics 27 | * 我们可以在 k+1...n 的范围内枚举 y (当 y= k ? n % y - k + 1 : 0) 33 | * 注意: 34 | * 1. 大数统计,可能出现溢出(并未尝试过,如果用 int 过了只能说明测试数据不行) 35 | * 2. 对于 k=0 要特殊处理,因为对于任意的(x, y),x%y 永远大于等于0,因此,当 k=0 时,答案为 n*n 36 | */ 37 | 38 | import java.io.BufferedInputStream; 39 | import java.util.*; 40 | 41 | public class Main { 42 | public static void main(String[] args) { 43 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 44 | while (sc.hasNext()) { 45 | long n = sc.nextLong(); 46 | long k = sc.nextLong(); 47 | long rst = 0; 48 | 49 | if (k == 0) { 50 | rst = n * n; 51 | } else { 52 | for (long y = k + 1; y <= n; y++) { 53 | rst += n / y * (y - k) // 周期出现的次数 * 周期内余数 >= k 的个数 54 | + (n % y >= k ? n % y - k + 1 : 0); // 剩下不满一个周期内 余数大于等于k 的个数 55 | } 56 | } 57 | 58 | System.out.println((rst)); 59 | } 60 | } 61 | } 62 | 63 | -------------------------------------------------------------------------------- /网易_星际穿越.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 航天飞行器是一项复杂而又精密的仪器,飞行器的损耗主要集中在发射和降落的过程, 4 | 科学家根据实验数据估计,如果在发射过程中,产生了 x 程度的损耗,那么在降落的过程中就会产生 x2 程度的损耗, 5 | 如果飞船的总损耗超过了它的耐久度,飞行器就会爆炸坠毁。 6 | 问一艘耐久度为 h 的飞行器,假设在飞行过程中不产生损耗,那么为了保证其可以安全的到达目的地, 7 | 只考虑整数解,至多发射过程中可以承受多少程度的损耗? 8 | 9 | 输入描述: 10 | 每个输入包含一个测试用例。每个测试用例包含一行一个整数 h (1 <= h <= 10^18)。 11 | 输出描述: 12 | 输出一行一个整数表示结果。 13 | 14 | 示例1 15 | 输入 16 | 10 17 | 输出 18 | 2 19 | */ 20 | 21 | /** 22 | * Approach: Binary Search 23 | * 数据量为 10^18,O(n)的方法必爆,因此想到使用 O(logn) 的二分法。 24 | * 二分法求最后一个符合条件的值(小于等于) 25 | * 因此使用:二分法上界的方法 26 | * 27 | * 关于如何使用二分法的详细解释可以参考: 28 | * https://github.com/cherryljr/NowCoder/blob/master/%E6%95%B0%E5%AD%97%E5%9C%A8%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E5%87%BA%E7%8E%B0%E7%9A%84%E6%AC%A1%E6%95%B0.java 29 | */ 30 | 31 | import java.io.BufferedInputStream; 32 | import java.util.*; 33 | 34 | public class Main { 35 | public static void main(String[] args) { 36 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 37 | long n = sc.nextLong(); 38 | System.out.println(binarySearch(n)); 39 | } 40 | 41 | private static long binarySearch(long n) { 42 | long start = 0, end = (long) Math.sqrt(n); 43 | while (start < end) { 44 | long mid = start + ((end - start + 1) >> 1); 45 | if (n >= mid * (mid + 1)) { 46 | start = mid; 47 | } else { 48 | end = mid - 1; 49 | } 50 | } 51 | return start; 52 | } 53 | } -------------------------------------------------------------------------------- /网易_最大和.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:32768K 4 | 5 | 在一个N*N的数组中寻找所有横,竖,左上到右下,右上到左下,四种方向的直线连续D个数字的和里面最大的值 6 | 输入描述: 7 | 每个测试输入包含1个测试用例,第一行包括两个整数 N 和 D : 8 | 3 <= N <= 100 9 | 1 <= D <= N 10 | 接下来有N行,每行N个数字d: 11 | 0 <= d <= 100 12 | 13 | 输出描述: 14 | 输出一个整数,表示找到的和的最大值 15 | 16 | 输入例子1: 17 | 4 2 18 | 87 98 79 61 19 | 10 27 95 70 20 | 20 64 73 29 21 | 71 65 15 0 22 | 23 | 输出例子1: 24 | 193 25 | */ 26 | 27 | /** 28 | * Approach: Brute Force 29 | * 暴力解的题目,纯粹考验编码能力... 30 | * 四个方向全部算一遍,求最大值即可(根据题目说明,数据不会溢出,不用开long) 31 | * 挺反感网易在笔试中出这种题目的... 32 | */ 33 | 34 | import java.util.Scanner; 35 | 36 | public class Main { 37 | 38 | public static void main(String[] args) { 39 | Scanner sc = new Scanner(System.in); 40 | int N = sc.nextInt(); 41 | int M = sc.nextInt(); 42 | int[][] data = new int[N][N]; 43 | for (int i = 0; i < N; i++) { 44 | for (int j = 0; j < N; j++) { 45 | data[i][j] = sc.nextInt(); 46 | } 47 | } 48 | int max = Integer.MIN_VALUE; 49 | for (int i = 0; i < N; i++) { 50 | for (int j = 0; j < N; j++) { 51 | max = Math.max(max, Math.max(leftToRight(data, i, j, M), rightTopToLeftBottom(data, i, j, M))); 52 | max = Math.max(max, Math.max(leftTopToRightBottom(data, i, j, M), TopToBottom(data, i, j, M))); 53 | } 54 | } 55 | System.out.println(max); 56 | } 57 | 58 | private static int leftToRight(int[][] data, int row, int col, int M) { 59 | int count = 0, sum = 0; 60 | for (int j = col; count < M && j < data[0].length; j++) { 61 | count++; 62 | sum += data[row][j]; 63 | } 64 | return count == M ? sum : -1; 65 | } 66 | 67 | private static int TopToBottom(int[][] data, int row, int col, int M) { 68 | int count = 0, sum = 0; 69 | for (int i = row; count < M && i < data.length; i++) { 70 | count++; 71 | sum += data[i][col]; 72 | } 73 | return count == M ? sum : -1; 74 | } 75 | 76 | private static int leftTopToRightBottom(int[][] data, int row, int col, int M) { 77 | int count = 0, sum = 0; 78 | for (int i = row, j = col; count < M && i < data.length && j < data[0].length; i++, j++) { 79 | count++; 80 | sum += data[i][j]; 81 | } 82 | return count == M ? sum : -1; 83 | } 84 | 85 | private static int rightTopToLeftBottom(int[][] data, int row, int col, int M) { 86 | int count = 0, sum = 0; 87 | for (int i = row, j = col; count < M && i >= 0 && j < data[0].length; i--, j++) { 88 | count++; 89 | sum += data[i][j]; 90 | } 91 | return count == M ? sum : -1; 92 | } 93 | } -------------------------------------------------------------------------------- /网易_混合颜料.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 你就是一个画家!你现在想绘制一幅画,但是你现在没有足够颜色的颜料。 4 | 为了让问题简单,我们用正整数表示不同颜色的颜料。 5 | 你知道这幅画需要的n种颜色的颜料,你现在可以去商店购买一些颜料, 6 | 但是商店不能保证能供应所有颜色的颜料,所以你需要自己混合一些颜料。 7 | 混合两种不一样的颜色A和颜色B颜料可以产生(A XOR B)这种颜色的颜料 8 | (新产生的颜料也可以用作继续混合产生新的颜色,XOR表示异或操作)。 9 | 本着勤俭节约的精神,你想购买更少的颜料就满足要求,所以兼职程序员的你需要编程来计算出最少需要购买几种颜色的颜料? 10 | 11 | 输入描述: 12 | 第一行为绘制这幅画需要的颜色种数n (1 ≤ n ≤ 50) 13 | 第二行为n个数xi(1 ≤ xi ≤ 1,000,000,000),表示需要的各种颜料. 14 | 输出描述: 15 | 输出最少需要在商店购买的颜料颜色种数,注意可能购买的颜色不一定会使用在画中,只是为了产生新的颜色。 16 | 17 | 示例1 18 | 输入 19 | 3 20 | 1 7 3 21 | 输出 22 | 3 23 | */ 24 | 25 | /** 26 | * Approach: Gaussian Elimination (高斯消元法) 27 | * 第一次看到这道题目并没有什么思路,于是观察其数据量。 28 | * Xi大小为 10^9 级别,因此不考虑 DP 解决,n 的级别为 50,因此折半枚举也不可能。 29 | * 再次思考,突然发现,这道题目的问法其实可以被改成: 30 | * 我们需要选取最少的整数个数,使得他们能代表集合中的全部整数。 31 | * 因此,这个问题实际上就被转换成了一个 矩阵求秩 的问题,我们需要求的就是 特征向量 的个数。 32 | * 对此,我们可以使用到 高斯消元法。 33 | * 时间复杂度为:O(32*n) => O(n) 34 | * 35 | * 类似问题 与 高斯消元法的详细解析可以参考: 36 | * https://github.com/cherryljr/NowCoder/blob/master/Maximum%20XOR%20Subset.java 37 | */ 38 | 39 | import java.io.BufferedInputStream; 40 | import java.util.Scanner; 41 | 42 | public class Main { 43 | public static void main(String[] args) { 44 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 45 | int n = sc.nextInt(); 46 | int[] nums = new int[n]; 47 | for (int i = 0; i < n; i++) { 48 | nums[i] = sc.nextInt(); 49 | } 50 | 51 | int index = 0; 52 | for (int i = 31; i >= 0; i--) { 53 | int maxValue = Integer.MIN_VALUE, maxIndex = index; 54 | for (int row = index; row < n; row++) { 55 | if (((nums[row] >>> i) & 1) == 1 && nums[row] > maxValue) { 56 | maxValue = nums[row]; 57 | maxIndex = row; 58 | } 59 | } 60 | if (maxValue == Integer.MIN_VALUE) { 61 | continue; 62 | } 63 | 64 | int temp = nums[index]; 65 | nums[index] = nums[maxIndex]; 66 | nums[maxIndex] = temp; 67 | maxIndex = index; 68 | 69 | for (int row = 0; row < n; row++) { 70 | if (row != maxIndex && ((nums[row] >>> i) & 1) == 1) { 71 | nums[row] ^= nums[maxIndex]; 72 | } 73 | } 74 | index++; 75 | } 76 | 77 | int rst = 0; 78 | // 计算特征向量的个数 79 | for (int row = 0; row < n; row++) { 80 | rst += nums[row] != 0 ? 1 : 0; 81 | } 82 | System.out.println(rst); 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /网易_牛牛的闹钟.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Approach: Binary Search 3 | * 把时间都转换为分钟计数 4 | * 最晚起床时间 = 上课时间 - 路上时间 5 | * 把所有闹钟时间排序后,二分查找最晚起床时间即可。 6 | * 7 | * 对于二分查找,这里我们可以自己写,或者使用 Java 自带的 binarySearch() 函数。 8 | * 自己实现的 binarySearchUpperBound(): 9 | * 查找时,采用二分查找法查找 begin-needTime 的上界,即最后一个小于 target 的数; 10 | * 11 | * 使用系统自带的Arrays.binarySearch(): 12 | * 笔试过程中,能用就用,毕竟省时间。 13 | * 只不过大家需要注意: 14 | * binarySearch()方法的返回值为: 15 | * 1. 如果找到关键字,则返回值为关键字在数组中的位置索引,且索引从0开始; 16 | * 2. 如果没有找到关键字,返回值为负的插入点值,所谓插入点值就是第一个 比关键字大 的元素在数组中的位置索引, 17 | * 而且这个位置索引从1开始。(这就是为什么当返回值为 负数 时,我们转成正数后是 -2) 18 | */ 19 | 20 | import java.io.BufferedInputStream; 21 | import java.util.*; 22 | 23 | public class Main { 24 | public static void main(String[] args) { 25 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 26 | int n = sc.nextInt(); 27 | int[] times = new int[n]; 28 | for (int i = 0; i < n; i++) { 29 | times[i] = sc.nextInt() * 60 + sc.nextInt(); 30 | } 31 | Arrays.sort(times); 32 | 33 | int needTime = sc.nextInt(); 34 | int begin = sc.nextInt() * 60 + sc.nextInt(); 35 | // int rst = Arrays.binarySearch(times, begin - needTime); 36 | // rst = rst > 0 ? times[rst] : times[-rst - 2]; 37 | int rst = times[binarySearchUpperBound(times, begin - needTime)]; 38 | System.out.println(rst / 60 + " " + rst % 60); 39 | } 40 | 41 | private static int binarySearchUpperBound(int[] times, int target) { 42 | int left = -1, right = times.length - 1; 43 | while (left < right) { 44 | int mid = left + ((right - left + 1) >> 1); 45 | if (target >= times[mid]) { 46 | left = mid; 47 | } else { 48 | right = mid - 1; 49 | } 50 | } 51 | return right; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /网易_矩形重叠.java: -------------------------------------------------------------------------------- 1 | /* 2 | 平面内有n个矩形, 第i个矩形的左下角坐标为(x1[i], y1[i]), 右上角坐标为(x2[i], y2[i])。 3 | 如果两个或者多个矩形有公共区域则认为它们是相互重叠的(不考虑边界和角落)。 4 | 请你计算出平面内重叠矩形数量最多的地方,有多少个矩形相互重叠。 5 | 6 | 输入描述: 7 | 输入包括五行。 8 | 第一行包括一个整数n(2 <= n <= 50), 表示矩形的个数。 9 | 第二行包括n个整数x1[i](-10^9 <= x1[i] <= 10^9),表示左下角的横坐标。 10 | 第三行包括n个整数y1[i](-10^9 <= y1[i] <= 10^9),表示左下角的纵坐标。 11 | 第四行包括n个整数x2[i](-10^9 <= x2[i] <= 10^9),表示右上角的横坐标。 12 | 第五行包括n个整数y2[i](-10^9 <= y2[i] <= 10^9),表示右上角的纵坐标。 13 | 14 | 输出描述: 15 | 输出一个正整数, 表示最多的地方有多少个矩形相互重叠,如果矩形都不互相重叠,输出1。 16 | 示例1 17 | 输入 18 | 2 19 | 0 90 20 | 0 90 21 | 100 200 22 | 100 200 23 | 输出 24 | 2 25 | */ 26 | 27 | /** 28 | * Approach: 离散化 + 几何判断 29 | * 这个题和线段重叠那个题很像,但是多了一维就不是那么好搞了,这里的 n 很小,那么肯定就是从这里下手了; 30 | * 第一反应就是随机化算法,即随机生成一个点,然后判断这个点在多少个矩形中,维护一个最大值。 31 | * 但是坐标的范围太大了,因此,要进行离散化,把X轴和Y轴的坐标离散化成小坐标; 32 | * 但是,这个离散化算法还是有问题,无法处理两个矩形共线或共点!多么希望我们随机化出来的点不在矩形的边界上啊; 33 | * 注意到,我们离散化出来的坐标都是挨在一起的,例如1后面一定是2,但是如果我们把离散化后的坐标扩大两倍, 34 | * 那么2后面就是4了,中间的3是没有使用的,而单位区域的中心是不会在矩形的边界上的, 35 | * 因此我们可以随机化单位区域的中心,以这个点去判断是否在矩形中,这样就解决了不考虑边界和角落这个条件, 36 | * 而把离散化后的坐标扩大两倍,举个例子,中心就是(2 + 4) / 2 = 3,中心可以确保都是正整数。 37 | */ 38 | 39 | import java.util.Arrays; 40 | import java.util.Scanner; 41 | 42 | public class Main { 43 | public static void main(String[] args) { 44 | Scanner sc = new Scanner(System.in); 45 | while (sc.hasNext()) { 46 | int n = sc.nextInt(); 47 | int[] xAxis = new int[n << 1]; 48 | int[] yAxis = new int[n << 1]; 49 | Rectangle[] rectangles = new Rectangle[n]; 50 | for (int i = 0; i < n; i++) { 51 | rectangles[i] = new Rectangle(); 52 | } 53 | // 左下角的横坐标 54 | for (int i = 0; i < n; i++) { 55 | xAxis[i] = rectangles[i].pos[0] = sc.nextInt(); 56 | } 57 | // 左下角的纵坐标 58 | for (int i = 0; i < n; i++) { 59 | yAxis[i] = rectangles[i].pos[1] = sc.nextInt(); 60 | } 61 | // 右上角的横坐标 62 | for (int i = 0; i < n; i++) { 63 | xAxis[n + i] = rectangles[i].pos[2] = sc.nextInt(); 64 | } 65 | // 右上角的纵坐标 66 | for (int i = 0; i < n; i++) { 67 | yAxis[n + i] = rectangles[i].pos[3] = sc.nextInt(); 68 | } 69 | Arrays.sort(xAxis); 70 | Arrays.sort(yAxis); 71 | 72 | // 离散化(离散化后的值需要 *2) 73 | for (int i = 0; i < n; i++) { 74 | for (int j = 0; j < 4; j++) { 75 | if ((j & 1) == 0) { 76 | rectangles[i].pos[j] = 2 * Arrays.binarySearch(xAxis, rectangles[i].pos[j]); 77 | } else { 78 | rectangles[i].pos[j] = 2 * Arrays.binarySearch(yAxis, rectangles[i].pos[j]); 79 | } 80 | } 81 | } 82 | 83 | int rst = 1; 84 | int bound = 2 * (2 * n - 1); 85 | for (int i = 0; i + 2 < bound; i += 2) { 86 | for (int j = 0; j + 2 < bound; j += 2) { 87 | int sum = 0; 88 | for (int k = 0; k < n; k++) { 89 | sum += isInRectangle(rectangles[k], i + 1, j + 1) ? 1 : 0; 90 | } 91 | rst = Math.max(rst, sum); 92 | } 93 | } 94 | System.out.println(rst); 95 | } 96 | } 97 | 98 | private static boolean isInRectangle(Rectangle rectangle, int x, int y) { 99 | return x > rectangle.pos[0] && x < rectangle.pos[2] && y > rectangle.pos[1] && y < rectangle.pos[3]; 100 | } 101 | 102 | static class Rectangle { 103 | int[] pos; 104 | 105 | Rectangle() { 106 | this.pos = new int[4]; 107 | } 108 | } 109 | 110 | } 111 | 112 | -------------------------------------------------------------------------------- /网易_被3整除.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:32768K 4 | 5 | 小Q得到一个神奇的数列: 1, 12, 123,...12345678910,1234567891011...。 6 | 并且小Q对于能否被3整除这个性质很感兴趣。 7 | 小Q现在希望你能帮他计算一下从数列的第l个到第r个(包含端点)有多少个数可以被3整除。 8 | 9 | 输入描述: 10 | 输入包括两个整数l和r(1 <= l <= r <= 1e9), 表示要求解的区间两端。 11 | 12 | 输出描述: 13 | 输出一个整数, 表示区间内能被3整除的数字个数。 14 | 15 | 输入例子1: 16 | 2 5 17 | 18 | 输出例子1: 19 | 3 20 | 21 | 例子说明1: 22 | 12, 123, 1234, 12345... 23 | 其中12, 123, 12345能被3整除。 24 | */ 25 | 26 | /** 27 | * Approach: Mathmatic 28 | * 看到数据范围就应该明白,这题不能递推,一是数组开不了这么大,二是递推时间很长。 29 | * 因此只能用数学方法算出来(这就是数据范围给我们做题的提示)。 30 | * 31 | * 一个数字n如果可以被3整除,那么 1, 2, 3, 4, ...分别对3取模得到 1, 2, 0, 1, 2, 0, ..., 32 | * 这个时候再看题中给出的序列 1, 12, 123, 1234, ... 33 | * 可以发现,1, 12, 123, 1234, 12345, ..., 34 | * 能被3整除的如下false, true, true, false, true, true, false, ... 35 | * 可以发现这个序列以3为周期,因此,直接按照这个规律写出代码即可。 36 | */ 37 | 38 | import java.io.BufferedInputStream; 39 | import java.util.*; 40 | 41 | public class Main { 42 | public static void main(String[] args) { 43 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 44 | int start = sc.nextInt(); 45 | int end = sc.nextInt(); 46 | System.out.println(fun(end) - fun(start - 1)); 47 | } 48 | 49 | public static int fun(int x) { 50 | return x / 3 * 2 + (x % 3 == 0 ? 0 : x % 3 == 1 ? 0 : 1); 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /网易_解救小易.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 有一片1000*1000的草地,小易初始站在(1,1)(最左上角的位置)。 4 | 小易在每一秒会横向或者纵向移动到相邻的草地上吃草(小易不会走出边界)。 5 | 大反派超超想去捕捉可爱的小易,他手里有n个陷阱。 6 | 第i个陷阱被安置在横坐标为xi ,纵坐标为yi 的位置上,小易一旦走入一个陷阱,将会被超超捕捉。 7 | 你为了去解救小易,需要知道小易最少多少秒可能会走入一个陷阱,从而提前解救小易。 8 | 9 | 输入描述: 10 | 第一行为一个整数n(n ≤ 1000),表示超超一共拥有n个陷阱。 11 | 第二行有n个整数xi,表示第i个陷阱的横坐标 12 | 第三行有n个整数yi,表示第i个陷阱的纵坐标 13 | 保证坐标都在草地范围内。 14 | 输出描述: 15 | 输出一个整数,表示小易最少可能多少秒就落入超超的陷阱 16 | 17 | 示例1 18 | 输入 19 | 3 20 | 4 6 8 21 | 1 2 1 22 | 输出 23 | 3 24 | */ 25 | 26 | /** 27 | * Approach: 直接计算最小距离 28 | * 刚刚看到以为是 BFS 求最短路径,结果发现 起始位置已经确定,陷阱位置确定。 29 | * 求最短距离...直接计算不就好了吗。 30 | * 小易的位置 到 陷阱 的位置,最短距离就是:min(Xi-1 + Yi-1) 31 | */ 32 | 33 | import java.io.BufferedInputStream; 34 | import java.util.Scanner; 35 | 36 | public class Main { 37 | public static void main(String[] args) { 38 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 39 | int n = sc.nextInt(); 40 | int min = 2000; 41 | int[][] trap = new int[n][2]; 42 | for (int i = 0; i < n; i++) { 43 | trap[i][0] = sc.nextInt() - 1; 44 | } 45 | for (int i = 0; i < n; i++) { 46 | trap[i][1] = sc.nextInt() - 1; 47 | min = Math.min(min, trap[i][0] + trap[i][1]); 48 | } 49 | System.out.println(min); 50 | } 51 | } -------------------------------------------------------------------------------- /网易_赛马.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:32768K 4 | 5 | 在一条无限长的跑道上,有N匹马在不同的位置上出发开始赛马。当开始赛马比赛后,所有的马开始以自己的速度一直匀速前进。 6 | 每匹马的速度都不一样,且全部是同样的均匀随机分布。在比赛中当某匹马追上了前面的某匹马时,被追上的马就出局。 7 | 请问按以上的规则比赛无限长的时间后,赛道上剩余的马匹数量的数学期望是多少 8 | 输入描述: 9 | 每个测试输入包含1个测试用例 10 | 输入只有一行,一个正整数N 11 | 1 <= N <= 1000 12 | 13 | 输出描述: 14 | 输出一个浮点数,精确到小数点后四位数字,表示剩余马匹数量的数学期望 15 | 16 | 输入例子1: 17 | 1 18 | 2 19 | 20 | 输出例子1: 21 | 1.0000 22 | 1.5000 23 | */ 24 | 25 | /** 26 | * Approach: Mathematics 27 | * 这是一个离散分布求数学期望的数学模型,重要的是求出剩余马匹数和对应的概率值 28 | * 本题切入点在于:赛道是无限长的,这就意味着只要后面马的速度更快,那么它一定就可以超过当前这匹马。 29 | * 因此我们只需要分析马的 速度 和 位置 即可。 30 | * 马的速度总可以从大到小排列 v1 > v2 > v3 >… > vN,不同速度的马存活与否和马出现的位置直接相关。 31 | * 速度最快 v1 的马无论出现在什么位置都能存活,存活概率为 1 32 | * 速度 v2 只有出现在最快马之前才能存活,由于马的速度是均匀随机分布,就是说v2在最快马之前之后等概,为 1/2 33 | * 速度 v3 出现在v2之前、v2 v1之间、v1之后也是等概,为 1/3 34 | * 因此可以通过 E = 1 + 1/2 + 1/3 + … + 1/n 求有马匹数量的数学期望 35 | * 36 | * 时间复杂度:O(n) 37 | * 空间复杂度:O(1) 38 | */ 39 | 40 | import java.util.Scanner; 41 | 42 | public class Main { 43 | public static void main(String[] args) { 44 | Scanner sc = new Scanner(System.in); 45 | while (sc.hasNext()) { 46 | int n = sc.nextInt(); 47 | 48 | double E = 0; 49 | while (n > 0) { 50 | E += 1 / (double)(n--); 51 | } 52 | System.out.printf("%.4f", E); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /网易_路灯.java: -------------------------------------------------------------------------------- 1 | 这个题目的实质就是求路灯之间的 最大间隔 。 2 | 只需要注意的是: 3 | 1. 第一盏路灯 和 最后一盏路灯 与道路的两边尽头距离就是d. 4 | 2. 中间部分,两盏路灯之间的距离 / 2 = d 5 | 故只需要对 路灯的位置 进行排序即可。 6 | 7 | /* 8 | 题目: 9 | 一条长l的笔直的街道上有n个路灯,若这条街的起点为0,终点为l,第i个路灯坐标为ai , 10 | 每盏灯可以覆盖到的最远距离为d,为了照明需求,所有灯的灯光必须覆盖整条街,但是为了省电,要是这个d最小, 11 | 请找到这个最小的d。 12 | 13 | 输入: 14 | 每组数据第一行两个整数n和l(n大于0小于等于1000,l小于等于1000000000大于0)。 15 | 第二行有n个整数(均大于等于0小于等于l),为每盏灯的坐标,多个路灯可以在同一点。 16 | 7 15 17 | 15 5 3 7 9 14 0 18 | 1 19 | 2 20 | 21 | 输出: 22 | 输出答案,保留两位小数。 23 | 2.50 24 | */ 25 | 26 | import java.util.*; 27 | 28 | public class Main{ 29 | public static void main(String[] args){ 30 | Scanner sc = new Scanner(System.in); 31 | int n = sc.nextInt(); 32 | int l = sc.nextInt(); 33 | int[] lights = new int[n]; 34 | 35 | for (int i = 0; i < n; i++) { 36 | lights[i] = sc.nextInt(); 37 | } 38 | Arrays.sort(lights); 39 | 40 | double max = Math.max(lights[0], l - lights[n - 1]); 41 | for (int i = 0; i < n - 1; i++) { 42 | max = (float) Math.max(max, (lights[i + 1] - lights[i]) / 2.0); 43 | } 44 | System.out.printf("%.2f", max); 45 | } 46 | 47 | 48 | } -------------------------------------------------------------------------------- /网易_迷路的牛牛.java: -------------------------------------------------------------------------------- 1 | /* 2 | 时间限制:1秒 3 | 空间限制:32768K 4 | 5 | 牛牛去犇犇老师家补课,出门的时候面向北方,但是现在他迷路了。 6 | 虽然他手里有一张地图,但是他需要知道自己面向哪个方向,请你帮帮他。 7 | 8 | 输入描述: 9 | 每个输入包含一个测试用例。 10 | 每个测试用例的第一行包含一个正整数,表示转方向的次数N(N<=1000)。 11 | 接下来的一行包含一个长度为N的字符串,由L和R组成,L表示向左转,R表示向右转。 12 | 13 | 输出描述: 14 | 输出牛牛最后面向的方向,N表示北,S表示南,E表示东,W表示西。 15 | 16 | 示例1 17 | 输入 18 | 3 19 | LRR 20 | 21 | 输出 22 | E 23 | */ 24 | 25 | /** 26 | * Approach: 模拟 27 | * 从北开始,顺时针旋转,将N,E,S,W分别记为:0,1,2,3 28 | * 左转相当于逆时针转,即-1;右转+1。 29 | * 避免结果为负数 加4 再对 4取余。 30 | */ 31 | 32 | import java.io.BufferedInputStream; 33 | import java.util.*; 34 | 35 | public class Main { 36 | public static final String DIRS = "NESW"; 37 | 38 | public static void main(String[] args) { 39 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 40 | int len = sc.nextInt(); 41 | String dirs = sc.next(); 42 | char[] chars = dirs.toCharArray(); 43 | int rst = 0; 44 | 45 | for (int i = 0; i < len; i++) { 46 | if (chars[i] == 'L') { 47 | // Turn left 48 | rst = (rst + 4 - 1) & 3; 49 | } else { 50 | // Turn right 51 | rst = (rst + 4 + 1) & 3; 52 | } 53 | } 54 | 55 | System.out.println(DIRS.charAt(rst)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /网易有道_洗牌.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 洗牌在生活中十分常见,现在需要写一个程序模拟洗牌的过程。 4 | 现在需要洗2n张牌,从上到下依次是第1张,第2张,第3张一直到第2n张。 5 | 首先,我们把这2n张牌分成两堆,左手拿着第1张到第n张(上半堆),右手拿着第n+1张到第2n张(下半堆)。 6 | 接着就开始洗牌的过程,先放下右手的最后一张牌,再放下左手的最后一张牌,接着放下右手的倒数第二张牌,再放下左手的倒数第二张牌, 7 | 直到最后放下左手的第一张牌。接着把牌合并起来就可以了。 8 | 例如有6张牌,最开始牌的序列是1,2,3,4,5,6。首先分成两组,左手拿着1,2,3;右手拿着4,5,6。 9 | 在洗牌过程中按顺序放下了6,3,5,2,4,1。把这六张牌再次合成一组牌之后,我们按照从上往下的顺序看这组牌,就变成了序列1,4,2,5,3,6。 10 | 现在给出一个原始牌组,请输出这副牌洗牌k次之后从上往下的序列。 11 | 12 | 输入描述: 13 | 第一行一个数T(T ≤ 100),表示数据组数。对于每组数据,第一行两个数n,k(1 ≤ n,k ≤ 100),接下来一行有2n个数a1,a2,...,a2n(1 ≤ ai ≤ 1000000000)。表示原始牌组从上到下的序列。 14 | 输出描述: 15 | 对于每组数据,输出一行,最终的序列。数字之间用空格隔开,不要在行末输出多余的空格。 16 | 17 | 示例1 18 | 输入 19 | 3 3 1 1 2 3 4 5 6 3 2 1 2 3 4 5 6 2 2 1 1 1 1 20 | 输出 21 | 1 4 2 5 3 6 1 5 4 3 2 6 1 1 1 1 22 | */ 23 | 24 | /** 25 | * Approach: 模拟题(找数学规律) 26 | * 每次读取一个数之后,算出他经过k次洗牌后的位置,只用一个长度为2n数组用来输出 27 | * 根据当前数的位置,可以算出经过一次洗牌后的位置: 28 | * 如果当前数小于等于n(即在左手),则他下次出现的位置是 2*当前位置-1 29 | * 如果当前位置大于n(即在右手),则他下次出现的位置是 2*(当前位置 - n) 30 | */ 31 | 32 | import java.io.BufferedInputStream; 33 | import java.util.Scanner; 34 | 35 | public class Main { 36 | public static void main(String[] args) { 37 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 38 | int T = sc.nextInt(); 39 | while (T-- > 0) { 40 | int n = sc.nextInt(); 41 | int k = sc.nextInt(); 42 | int[] rst = new int[2 * n]; 43 | for (int i = 0; i < 2 * n; i++) { 44 | int currPos = i + 1; 45 | for (int j = 0; j < k; j++) { 46 | if (currPos <= n) { 47 | currPos = 2 * currPos - 1; 48 | } else { 49 | currPos = 2 * (currPos - n); 50 | } 51 | } 52 | rst[currPos - 1] = sc.nextInt(); 53 | } 54 | 55 | if (rst.length > 0) { 56 | System.out.print(rst[0]); 57 | } 58 | for (int i = 1; i < 2 * n; i++) { 59 | System.out.print(" " + rst[i]); 60 | } 61 | System.out.println(); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /美丽的项链.java: -------------------------------------------------------------------------------- 1 | /* 2 | 妞妞参加了Nowcoder Girl女生编程挑战赛, 但是很遗憾, 她没能得到她最喜欢的黑天鹅水晶项链。 3 | 于是妞妞决定自己来制作一条美丽的项链。一条美丽的项链需要满足以下条件: 4 | 1、需要使用n种特定的水晶宝珠 5 | 2、第i种水晶宝珠的数量不能少于li颗, 也不能多于ri颗 6 | 3、一条美丽的项链由m颗宝珠组成 7 | 妞妞意识到满足条件的项链种数可能会很多, 所以希望你来帮助她计算一共有多少种制作美丽的项链的方案。 8 | 9 | 输入描述: 10 | 输入包括n+1行, 第一行包括两个正整数(1 <= n <= 20, 1 <= m <= 100), 表示水晶宝珠的种数和一条美丽的项链需要的水晶宝珠的数量。 11 | 接下来的n行, 每行两个整数li, ri(0 <= li <= ri <= 10), 表示第i种宝珠的数量限制区间。 12 | 13 | 输出描述: 14 | 输出一个整数, 表示满足限定条件的方案数。保证答案在64位整数范围内。 15 | 示例1 16 | 输入 17 | 3 5 18 | 0 3 19 | 0 3 20 | 0 3 21 | 输出 22 | 12 23 | 24 | 备注: 25 | 对于两种方案,当有任意一种水晶宝珠个数不同,就视为两种不同方案。 26 | 27 | OJ地址:https://www.nowcoder.com/questionTerminal/e7e0230b12de4239a7f547a01d731522 28 | */ 29 | 30 | /** 31 | * Approach: Multiple Backpack 32 | */ 33 | 34 | import java.io.BufferedInputStream; 35 | import java.util.*; 36 | 37 | public class Main { 38 | public static void main(String[] args) { 39 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 40 | int n = sc.nextInt(); 41 | int m = sc.nextInt(); 42 | int[][] jewelry = new int[n][2]; 43 | long[][] dp = new long[n][m + 1]; 44 | for (int i = 0; i < n; i++) { 45 | jewelry[i][0] = sc.nextInt(); 46 | jewelry[i][1] = sc.nextInt(); 47 | } 48 | 49 | // Initialize 50 | for (int i = jewelry[0][0]; i <= jewelry[0][1]; i++) { 51 | dp[0][i] = 1; 52 | } 53 | // Function 54 | for (int i = 1; i < n; i++) { 55 | for (int j = 0; j <= m; j++) { 56 | for (int k = jewelry[i][0]; k <= jewelry[i][1]; k++) { 57 | if (j >= k) { 58 | dp[i][j] += dp[i - 1][j - k]; 59 | } 60 | } 61 | } 62 | } 63 | 64 | System.out.println(dp[n - 1][m]); 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /美团_丢失的三个数.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 现在有一个数组,其值为从1到10000的连续增长的数字。 4 | 出于某次偶然操作,导致这个数组中丢失了某三个元素,同时顺序被打乱, 5 | 现在需要你用最快的方法找出丢失的这三个元素,并且将这三个元素根据从小到大重新拼接为一个新数字,计算其除以7的余数。 6 | 例:丢失的元素为336,10,8435,得到的新数字为103368435,除以七的余数为2。 7 | 8 | 输入描述: 9 | 输入数据为一行,包含9997个数字,空格隔开。 10 | 输出描述: 11 | 输出为一行,包含一个数字。 12 | 13 | 示例1 14 | 输入 15 | 同题设例子输入 16 | 输出 17 | 2 18 | */ 19 | 20 | /** 21 | * Approach: Traverse (make note) 22 | * 因为只需要找出哪三个数字缺少了,并且数字出现的范围已知。 23 | * 所以利用一个 boolean[] 记录每个数字是否出现过即可,(以数字的值作为下标) 24 | * 然后从前往后遍历时顺序自然是从小到大的,根本无需排序。 25 | * 将其拼接起来后,%7 就是答案了 26 | * 27 | * 时间复杂度:O(n) 28 | */ 29 | 30 | import java.io.BufferedReader; 31 | import java.io.InputStreamReader; 32 | import java.math.BigDecimal; 33 | import java.math.BigInteger; 34 | import java.util.*; 35 | 36 | public class Main { 37 | public static void main(String[] args) { 38 | InputReader input = new InputReader(); 39 | boolean[] nums = new boolean[10001]; 40 | for (int i = 0; i < 9997; i++) { 41 | nums[input.nextInt()] = true; 42 | } 43 | 44 | StringBuilder sb = new StringBuilder(); 45 | for (int i = 0; i < nums.length; i++) { 46 | if (!nums[i]) { 47 | sb.append(i); 48 | } 49 | } 50 | System.out.println(new Long(sb.toString()) % 7); 51 | } 52 | 53 | static class InputReader { 54 | BufferedReader buffer; 55 | StringTokenizer token; 56 | 57 | InputReader() { 58 | buffer = new BufferedReader(new InputStreamReader(System.in)); 59 | } 60 | 61 | boolean hasNext() { 62 | while (token == null || !token.hasMoreElements()) { 63 | try { 64 | token = new StringTokenizer(buffer.readLine()); 65 | } catch (Exception e) { 66 | return false; 67 | } 68 | } 69 | return true; 70 | } 71 | 72 | String next() { 73 | if (hasNext()) return token.nextToken(); 74 | return null; 75 | } 76 | 77 | int nextInt() { 78 | return Integer.parseInt(next()); 79 | } 80 | 81 | long nextLong() { 82 | return Long.parseLong(next()); 83 | } 84 | 85 | double nextDouble() { 86 | return Double.parseDouble(next()); 87 | } 88 | 89 | BigInteger nextBigInteger() { 90 | return new BigInteger(next()); 91 | } 92 | 93 | BigDecimal nextBigDecimal() { 94 | return new BigDecimal(next()); 95 | } 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /能转换的最长字串(仅行字符a,b).java: -------------------------------------------------------------------------------- 1 | /* 2 | 有一个仅包含’a’和’b’两种字符的字符串s,长度为n,每次操作可以把一个字符做一次转换 3 | (把一个’a’设置为’b’,或者把一个’b’置成’a’);但是操作的次数有上限m, 4 | 问在有限的操作数范围内,能够得到最大连续的相同字符的子串的长度是多少。 5 | 6 | 输入描述: 7 | 第一行两个整数 n , m (1<=m<=n<=50000),第二行为长度为n且只包含’a’和’b’的字符串s。 8 | 输出描述: 9 | 输出在操作次数不超过 m 的情况下,能够得到的 最大连续 全’a’子串或全’b’子串的长度。 10 | 11 | 示例1 12 | 输入 13 | 8 1 14 | aabaabaa 15 | 输出 16 | 5 17 | 说明 18 | 把第一个 'b' 或者第二个 'b' 置成 'a',可得到长度为 5 的全 'a' 子串。 19 | */ 20 | 21 | /** 22 | * Approach: Binary Search 23 | * 第一眼看到以为是一个 区间DP 的问题。时间复杂度为 O(n^2) 写了一下发现超时了... 24 | * 因此我们只能考虑 O(nlogn) 的解法了。 25 | * 26 | * 重新分析题目: 27 | * 该字符串非 a 即 b 也就是说在区间 left~right 之间把所有字符变为 a 所需的步骤数是 该区间内 b 的数量。反之亦然. 28 | * 用数组 count_a[i] 表示字符串中位置区间 0~i-1 包含的 a 的个数 (为了方便起见count_a数组大小为 n+1) 29 | * 则 区间left~right 的 a 的个数为 count_a[right] - count_a[left-1] 30 | * b 的个数可以利用 a 的个数算出即区间 left~right 的 b 的个数为: 区间总字符个数(区间长度)- 字符'a'的个数 31 | * right - left + 1 - (count_a[right] - count_a[left-1]) 32 | * 在区间 left~right 的 a 和 b 的个数已知的情况下 33 | * 若 区间长度step内的 a 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的字符串 b 34 | * 若 区间长度step内的 b 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的字符串 a 35 | * 归纳为:若 区间长度step内的 b 或 a 的个数 <= m 则 可以通过 m 个步骤 产生 长度为step的字符串 36 | * 37 | * 这样问题就转成成求一个 最长字符串长度(区间长度step) 使得 a 或 b 的个数 <= m。 38 | * 在已有 count_a[] 的情况下,因为 step 是单调的,所以我们可以对其进行二分从而实现快速确定区间长度。 39 | * 检查一个长度step是否可行的时间复杂度为O(n);二分搜索的时间复杂度为O(logn)。 40 | * 因此,该方法总的时间复杂度为 O(nlogn) 41 | * 42 | * 注意边界条件的处理:通常直接举个例子调试一遍就行了。 43 | * 对于 二分法 不清楚的可以参考: 44 | * https://github.com/cherryljr/NowCoder/blob/master/%E6%95%B0%E5%AD%97%E5%9C%A8%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E4%B8%AD%E5%87%BA%E7%8E%B0%E7%9A%84%E6%AC%A1%E6%95%B0.java 45 | */ 46 | 47 | import java.io.BufferedInputStream; 48 | import java.util.*; 49 | 50 | public class Main { 51 | public static void main(String[] args) { 52 | Scanner sc = new Scanner(new BufferedInputStream(System.in)); 53 | int n = sc.nextInt(); 54 | int m = sc.nextInt(); 55 | String str = sc.next(); 56 | char[] arr = str.toCharArray(); 57 | // 计算字符'a'个数的前缀和数组 58 | int[] count_a = new int[n + 1]; 59 | for (int i = 1; i <= n; i++) { 60 | count_a[i] = count_a[i - 1] + (arr[i - 1] == 'a' ? 1 : 0); 61 | } 62 | 63 | // 二分法求上界 64 | int left = 0, right = n; 65 | while (left < right) { 66 | int mid = left + ((right - left + 1) >> 1); 67 | if (canBeTransformed(mid, count_a, m)) { 68 | left = mid; 69 | } else { 70 | right = mid - 1; 71 | } 72 | } 73 | System.out.println(left); 74 | } 75 | 76 | private static boolean canBeTransformed(int step, int[] count_a, int m) { 77 | for (int i = 0; i + step < count_a.length; i++) { 78 | int a = count_a[i + step] - count_a[i]; // 区间内字符'a'的个数 79 | // 如果将 'a' 全部转换成 'b' 的次数 <= m 返回 true 80 | if (a <= m) { 81 | return true; 82 | } 83 | // 如果将 'b' 全部转换成 'a' 的次数 <= m 返回 true 84 | if (step - a <= m) { 85 | return true; 86 | } 87 | } 88 | return false; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /腾讯_小Q的歌单.java: -------------------------------------------------------------------------------- 1 | /* 2 | 小Q有X首长度为A的不同的歌和Y首长度为B的不同的歌, 3 | 现在小Q想用这些歌组成一个总长度正好为K的歌单,每首歌最多只能在歌单中出现一次, 4 | 在不考虑歌单内歌曲的先后顺序的情况下,请问有多少种组成歌单的方法。 5 | 6 | 输入描述: 7 | 每个输入包含一个测试用例。 8 | 每个测试用例的第一行包含一个整数,表示歌单的总长度K(1<=K<=1000)。 9 | 接下来的一行包含四个正整数,分别表示歌的第一种长度A(A<=10)和数量X(X<=100)以及歌的第二种长度B(B<=10)和数量Y(Y<=100)。保证A不等于B。 10 | 11 | 输出描述: 12 | 输出一个整数,表示组成歌单的方法取模。因为答案可能会很大,输出对1000000007取模的结果。 13 | 14 | 示例1 15 | 输入 16 | 5 17 | 2 3 3 3 18 | 输出 19 | 9 20 | */ 21 | 22 | /** 23 | * Approach: DP 24 | * 这道题目的实质是一个 DP 问题,考察的是杨辉三角的构造。 25 | * 只有两种长度的歌曲,且方案数跟顺序无关,很明显就是使用 C(m, n) 的方法来计算。 26 | * 因此问题就转换成了:如何高效地计算出 C(m, n). 27 | * 这里可以利用数学归纳法: 28 | * 由C(n,k) = C(n-1,k) + C(n-1,k-1) 29 | * 因此实际上是对应于杨辉三角: 30 | * 1 31 | * 1 1 32 | * 1 2 1 33 | * 1 3 3 1 34 | * 1 4 6 4 1 35 | * ........... 36 | * 37 | * 我们只需要构造出需要的杨辉三角,然后直接取出需要的值计算即可。 38 | */ 39 | import java.util.Scanner; 40 | 41 | public class Main { 42 | public static final int MOD = 1000000007; 43 | 44 | public static void main(String[] args) { 45 | Scanner sc = new Scanner(System.in); 46 | while (sc.hasNext()) { 47 | int K = sc.nextInt(); 48 | int A = sc.nextInt(), X = sc.nextInt(); 49 | int B = sc.nextInt(), Y = sc.nextInt(); 50 | System.out.println(getResult(K, A, X, B, Y)); 51 | } 52 | sc.close(); 53 | } 54 | 55 | private static int getResult(int K, int A, int X, int B, int Y) { 56 | // 构建杨辉三角 57 | long[][] tri = new long[105][105]; 58 | tri[0][0] = 1; 59 | for (int i = 1; i < 105; i++) { 60 | tri[i][0] = 1; 61 | for (int j = 1; j < 105; j++) { 62 | tri[i][j] = (tri[i - 1][j - 1] + tri[i - 1][j]) % MOD; 63 | } 64 | } 65 | 66 | long rst = 0; 67 | // 其中 A 长度的歌曲选取 i 首 68 | for (int i = 0; i <= X; i++) { 69 | int left = K - A * i; 70 | // 剩余的长度必须 >= 0 且正好能够被 B 长度的歌曲使用完 71 | if (left >= 0 && left % B == 0 && left / B <= Y) { 72 | // C(X, i) * C(Y, left/B) 73 | rst = (rst + tri[X][i] * tri[Y][left / B]) % MOD; 74 | } 75 | } 76 | return (int)rst; 77 | } 78 | } -------------------------------------------------------------------------------- /腾讯_翻转数列.java: -------------------------------------------------------------------------------- 1 | /* 2 | 小Q定义了一种数列称为翻转数列: 3 | 给定整数n和m, 满足n能被2m整除。对于一串连续递增整数数列1, 2, 3, 4..., 每隔m个符号翻转一次, 最初符号为'-';。 4 | 例如n = 8, m = 2, 数列就是: -1, -2, +3, +4, -5, -6, +7, +8. 5 | 而n = 4, m = 1, 数列就是: -1, +2, -3, + 4. 6 | 小Q现在希望你能帮他算算前n项和为多少。 7 | 8 | 输入描述: 9 | 输入包括两个整数n和m(2 <= n <= 109, 1 <= m), 并且满足n能被2m整除。 10 | 输出描述: 11 | 输出一个整数, 表示前n项和。 12 | 13 | 输入例子1: 14 | 8 2 15 | 输出例子1: 16 | 8 17 | */ 18 | 19 | /** 20 | * Approach: Mathematics 21 | * 找规律的问题。这一点从题目的数据规模也可以看出。 22 | * 因为 n % 2m == 0 所以我们可以把 n 个数分成 n / m 组, 23 | * 并且我们可以发现每组之和是固定的,都等于 m^2. 24 | * 因此结果为: n / m * m^2 = n * m / 2 25 | * 26 | * 时间复杂度:O(n) 27 | */ 28 | import java.util.Scanner; 29 | 30 | public class Main { 31 | public static void main(String[] args) { 32 | Scanner sc = new Scanner(System.in); 33 | while (sc.hasNext()) { 34 | long n = sc.nextInt(), m = sc.nextInt(); 35 | System.out.println(n * m >> 1); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /腾讯_贪吃的小Q.java: -------------------------------------------------------------------------------- 1 | /* 2 | 小Q的父母要出差N天,走之前给小Q留下了M块巧克力。 3 | 小Q决定每天吃的巧克力数量不少于前一天吃的一半, 4 | 但是他又不想在父母回来之前的某一天没有巧克力吃, 5 | 请问他第一天最多能吃多少块巧克力 6 | 7 | 输入描述: 8 | 每个输入包含一个测试用例。 9 | 每个测试用例的第一行包含两个正整数,表示父母出差的天数N(N<=50000)和巧克力的数量M(N<=M<=100000)。 10 | 11 | 输出描述: 12 | 输出一个数表示小Q第一天最多能吃多少块巧克力。 13 | 14 | 输入例子1: 15 | 3 7 16 | 17 | 输出例子1: 18 | 4 19 | */ 20 | 21 | /** 22 | * Approach: Binary Search (Upper Bound) 23 | * 题目要求的是:最多能够次多少块巧克力。 24 | * 即不大于 target 的最大值,因此对应的就是使用 二分法求上界。 25 | */ 26 | import java.util.Scanner; 27 | 28 | public class Main { 29 | public static void main(String[] args) { 30 | Scanner sc = new Scanner(System.in); 31 | while (sc.hasNext()) { 32 | int n = sc.nextInt(), m = sc.nextInt(); 33 | int start = 1, end = m; 34 | while (start < end) { 35 | int mid = start + (end - start + 1 >> 1); 36 | int count = getSum(mid, n); 37 | if (m >= count) { 38 | start = mid; 39 | } else { 40 | end = mid - 1; 41 | } 42 | } 43 | System.out.println(end); 44 | } 45 | } 46 | 47 | private static int getSum(int x, int n) { 48 | int sum = 0; 49 | for (int i = 0; i < n; i++) { 50 | sum += x; 51 | x = (x + 1) >> 1; // 向上取整 52 | } 53 | return sum; 54 | } 55 | } -------------------------------------------------------------------------------- /蘑菇街_回文串.java: -------------------------------------------------------------------------------- 1 | /* 2 | 回文串 3 | 时间限制:1秒 4 | 空间限制:32768K 5 | 6 | 给定一个字符串,问是否能通过添加一个字母将其变为回文串。 7 | 8 | 输入描述: 9 | 一行一个由小写字母构成的字符串,字符串长度小于等于10。 10 | 11 | 输出描述: 12 | 输出答案(YES\NO). 13 | 14 | 输入例子1: 15 | coco 16 | 17 | 输出例子1: 18 | YES 19 | */ 20 | 21 | /** 22 | * Approach: Recursion 23 | * 根据数据规模,第一反应就是使用递归来解决。 24 | * 具体解析看代码注释即可。 25 | */ 26 | 27 | import java.util.*; 28 | 29 | public class Main { 30 | public static void main(String[] args) { 31 | Scanner sc = new Scanner(System.in); 32 | while (sc.hasNext()) { 33 | String str = sc.next(); 34 | System.out.println(canBeTransformed(str, 0, str.length() - 1, false) ? "YES" : "NO"); 35 | } 36 | } 37 | 38 | private static boolean canBeTransformed(String str, int start, int end, boolean flag) { 39 | if (start >= end) { 40 | return true; 41 | } 42 | 43 | // 如果两个字符相等,则可以进一步缩小 44 | if (str.charAt(start) == str.charAt(end)) { 45 | return canBeTransformed(str, start + 1, end - 1, flag); 46 | } else if (!flag) { 47 | // 如果不相等,并且还未插入过字符,则可以选择插入与 start / end 位置相同的字符,去尝试构成回文串 48 | flag = true; 49 | return canBeTransformed(str, start + 1, end, flag) || canBeTransformed(str, start, end - 1, flag); 50 | } else { 51 | return false; 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * Approach 2: DP (Longest Common SubSequence) 58 | * 因为无后效性,所以这道题目实际上是一道 DP 问题(给出这个数据量真的是...) 59 | * 看到回文串,我们会下意识地将它进行 reverse,然后求原串和反串的 最长公共子序列 , 60 | * 如果 最长公共子序列的长度 是 原字符串的长度减一 ,那么就是可行的 61 | * (题目明确要求添加一个字符) 62 | * 63 | * 时间复杂度:O(MN) 64 | * 空间复杂度:O(MN) 65 | * 66 | * Longest Common Subsequence: 67 | * https://github.com/cherryljr/LintCode/blob/master/Longest%20Common%20Subsequence.java 68 | */ 69 | 70 | import java.util.*; 71 | 72 | public class Main { 73 | public static void main(String[] args) { 74 | Scanner sc = new Scanner(System.in); 75 | while (sc.hasNext()) { 76 | String str = sc.next(); 77 | StringBuilder reverseStr = new StringBuilder(str).reverse(); 78 | int len = getLCS(str, reverseStr.toString()); 79 | System.out.println(len + 1 == str.length() ? "YES" : "NO"); 80 | } 81 | } 82 | 83 | private static int getLCS(String str1, String str2) { 84 | int n = str1.length(), m = str2.length(); 85 | int[][] dp = new int[n + 1][m + 1]; 86 | 87 | for (int i = 1; i <= n; i++) { 88 | for (int j = 1; j <= m; j++) { 89 | if (str1.charAt(i - 1) == str2.charAt(j - 1)) { 90 | dp[i][j] = dp[i - 1][j - 1] + 1; 91 | } else { 92 | dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); 93 | } 94 | } 95 | } 96 | return dp[n][m]; 97 | } 98 | } -------------------------------------------------------------------------------- /蘑菇街_聊天.java: -------------------------------------------------------------------------------- 1 | /* 2 | 聊天 3 | 时间限制:1秒 4 | 空间限制:32768K 5 | 6 | A和B是好友,他们经常在空闲时间聊天,A的空闲时间为[a1 ,b1 ],[a2 ,b2 ]..[ap ,bp ]。 7 | B的空闲时间是[c1 +t,d1 +t]..[cq +t,dq +t],这里t为B的起床时间。这些时间包括了边界点。 8 | B的起床时间为[l,r]的一个时刻。若一个起床时间能使两人在某一时刻聊天,那么这个时间就是合适的,问有多少个合适的起床时间? 9 | 10 | 输入描述: 11 | 第一行数据四个整数:p,q,l,r(1≤p,q≤50,0≤l≤r≤1000)。接下来p行数据每一行有一对整数ai,bi(0≤ai+1>bi,ci+1>di) 12 | 13 | 输出描述: 14 | 输出答案个数 15 | 16 | 输入例子1: 17 | 2 3 0 20 18 | 15 17 19 | 23 26 20 | 1 4 21 | 7 11 22 | 15 17 23 | 24 | 输出例子1: 25 | 20 26 | */ 27 | 28 | /** 29 | * Approach: Simulation (Find Overlap Interval) 30 | * 没啥水平的题...初看区间类题,以为会涉及到扫描线或者线段树结果发现直接暴力遍历一遍 31 | * 判断两个区间是否有交集即可... 32 | * 题目描述有些坑爹,这边直接做了下修改。 33 | * 34 | * 时间复杂度:O(N*M*(R-L)) 35 | */ 36 | 37 | import java.util.*; 38 | 39 | public class Main { 40 | public static void main(String[] args) { 41 | Scanner sc = new Scanner(System.in); 42 | while (sc.hasNext()) { 43 | int p = sc.nextInt(), q = sc.nextInt(); 44 | int l = sc.nextInt(), r = sc.nextInt(); 45 | int[][] A = new int[p][2]; 46 | int[][] B = new int[q][2]; 47 | for (int i = 0; i < p; i++) { 48 | A[i][0] = sc.nextInt(); 49 | A[i][1] = sc.nextInt(); 50 | } 51 | for (int i = 0; i < q; i++) { 52 | B[i][0] = sc.nextInt(); 53 | B[i][1] = sc.nextInt(); 54 | } 55 | 56 | int count = 0; 57 | for (int i = l; i <= r; i++) { 58 | if (isProperTime(A, B, i)) { 59 | count++; 60 | } 61 | } 62 | System.out.println(count); 63 | } 64 | } 65 | 66 | // 判断 A[] 和 B[] 中所有的时间区间,只要有一个区间有交集即可 67 | private static boolean isProperTime(int[][] A, int[][] B, int time) { 68 | for (int i = 0; i < A.length; i++) { 69 | for (int j = 0; j < B.length; j++) { 70 | // 如果发现 A[i] 和 B[j] 这两个区间有交集,则返回 true 71 | if ((B[j][0] + time >= A[i][0] && B[j][0] + time <= A[i][1]) 72 | || (A[i][0] >= B[j][0] + time && A[i][0] <= B[j][1] + time)) { 73 | return true; 74 | } 75 | } 76 | } 77 | return false; 78 | } 79 | } -------------------------------------------------------------------------------- /调整数组顺序使奇数位于偶数前面.java: -------------------------------------------------------------------------------- 1 | /* 2 | 题目描述 3 | 输入一个整数数组,实现一个函数来调整该数组中数字的顺序, 4 | 使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分, 5 | 并保证奇数和奇数,偶数和偶数之间的相对位置不变。 6 | */ 7 | 8 | /** 9 | * Approach 1: Traverse 10 | * 本题要求各个数之间的相对位置不能发生变换,这就以为这这是一个稳定的 Partition 操作。 11 | * 因此不能够使用 QuickSelect. 12 | * 对此最简单暴力的方法就是: 13 | * 新建一个数组空间,然后进行扫描分类即可。 14 | * 15 | * 时间复杂度:O(n) 16 | * 空间复杂度:O(n) 17 | */ 18 | public class Solution { 19 | public void reOrderArray(int [] array) { 20 | int[] newArray = new int[array.length]; 21 | int oddPointer = 0, oddCount = 0; 22 | for (int i = 0; i < array.length; i++) { 23 | if ((array[i] & 1) == 1) { 24 | oddCount++; 25 | } 26 | } 27 | 28 | for (int i = 0; i < array.length; i++) { 29 | if ((array[i] & 1) == 1) { 30 | newArray[oddPointer++] = array[i]; 31 | } else { 32 | newArray[oddCount++] = array[i]; 33 | } 34 | } 35 | System.arraycopy(newArray, 0, array, 0, array.length); 36 | } 37 | } 38 | 39 | /** 40 | * Approach 2: Bubble Sort 41 | * 如果要求空间复杂度为 O(1) 的话,那么我们只能使用稳定排序的实现了。 42 | * 这里使用了 冒泡排序 的思想。如果前偶数后奇数就发生一次交换。 43 | * 这样就能够使得所有奇数在左,所有偶数在右。 44 | * 45 | * 时间复杂度:O(n^2) 46 | * 空间复杂度:O(1) 47 | */ 48 | public class Solution { 49 | public void reOrderArray(int [] array) { 50 | for (int end = array.length - 1; end > 0; end--) { 51 | for (int start = 0; start < end; start++) { 52 | if ((array[start] & 1) == 0 && (array[start + 1] & 1) == 1) { 53 | int temp = array[start]; 54 | array[start] = array[start + 1]; 55 | array[start + 1] = temp; 56 | } 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /购物单.java: -------------------------------------------------------------------------------- 1 | 背包问题 2 | 01背包问题的扩展,增加了一些限制而已。 3 | 总体而言与 K Sum 相同 4 | 5 | /* 6 | 王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件, 7 | 附件是从属于某个主件的,下表就是一些主件与附件的例子: 8 | 主件 附件 9 | 电脑 打印机,扫描仪 10 | 书柜 图书 11 | 书桌 台灯,文具 12 | 工作椅 无 13 | 如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。 14 | 王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 ~ 5 表示,第 5 等最重要。 15 | 他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。 16 | 他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。 17 | 18 | 设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j1, j2,……, jk,则所求的总和为: 19 | v[j1]*w[j1] + v[j2]*w[j2] + … + v[jk]*w[jk]。(其中 * 为乘号) 20 | 请你帮助王强设计一个满足要求的购物单。 21 |   22 | 输入描述: 23 | 输入的第 1 行,为两个正整数,用一个空格隔开:N m 24 | (其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。) 25 | 从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q 26 | (其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。 27 | 如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号) 28 | 29 | 输出描述: 30 | 输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。 31 | 32 | 示例1 33 | 34 | 输入 35 | 1000 5 36 | 800 2 0 37 | 400 5 1 38 | 300 5 1 39 | 400 3 0 40 | 500 2 0 41 | 42 | 输出 43 | 2200 44 | */ 45 | 46 | import java.util.Scanner; 47 | 48 | public class Main{ 49 | public static void main(String[] args) { 50 | Scanner sc = new Scanner(System.in); 51 | int money = sc.nextInt(); 52 | int num = sc.nextInt(); 53 | int[] price = new int[num + 1]; 54 | int[] val = new int[num + 1]; 55 | int[] q = new int[num + 1]; 56 | for (int i = 1; i <= num; i++) { 57 | price[i] = sc.nextInt(); 58 | val[i] = sc.nextInt() * price[i]; 59 | q[i] = sc.nextInt(); 60 | } 61 | 62 | int[][] dp = new int[num + 1][money + 1]; 63 | for (int i = 1; i <= num; i++) { 64 | for (int j = 1; j <= money; j++) { 65 | if(q[i] == 0) 66 | { 67 | if(price[i] <= j) 68 | dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-price[i]] + val[i]); 69 | } 70 | if(q[i] > 0) 71 | { 72 | if(price[i] + price[q[i]] <= j) 73 | dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-price[i]-price[q[i]]] + val[i] + val[q[i]]); 74 | } 75 | } 76 | 77 | } 78 | 79 | System.out.println(dp[num][money]); 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /进制转换.java: -------------------------------------------------------------------------------- 1 | 该题目可以通过直接调用 Java 自带的进制转换方法来实现。 2 | 并且可以进一步扩展为: 3 | 1. 十进制数转 N 进制数。 4 | 1.1 解决方法1:打表解决 5 | 1.2 解决方法2:调用 Java 中 BigInteger 类自带的方法 6 | 2. M进制 转 N进制 7 | 直接调用 Native Method 解决 8 | 9 | /* 10 | 写出一个程序,接受一个十六进制的数值字符串,输出该数值的十进制字符串。(多组同时输入 ) 11 | */ 12 | 13 | import java.util.*; 14 | 15 | public class Main{ 16 | public static void main(String[] args) { 17 | Scanner sc=new Scanner(System.in); 18 | while(sc.hasNext()) 19 | { 20 | String s = sc.next().substring(2); 21 | System.out.println(Integer.parseInt(s, 16)); 22 | } 23 | 24 | } 25 | } 26 | 27 | /* 28 | 扩张问题 1 29 | 给定一个十进制数M,以及需要转换的进制数N。将十进制数 M 转化为 N 进制数 30 | 31 | 输入描述: 32 | 输入为一行,M(32位整数)、N(2 ≤ N ≤ 16),以空格隔开。 33 | 34 | 输出描述: 35 | 为每个测试实例输出转换后的数,每个输出占一行。如果N大于9,则对应的数字规则参考16进制(比如,10用A表示,等等) 36 | 37 | 示例 38 | 输入 39 | 7 2 40 | 41 | 输出 42 | 111 43 | */ 44 | 45 | // Solution 1: 打表 46 | import java.util.Scanner; 47 | 48 | public class Main { 49 |     public static void main(String[] args) { 50 |         Scanner sc = new Scanner(System.in); 51 |         int num = sc.nextInt(); 52 |         int radix = sc.nextInt(); 53 | // 打表 54 |         char[] chars = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 55 |         // 注意对负数进行处理 56 | boolean negative = false; 57 |         if (num < 0) { 58 | negative = true; 59 | } 60 |         StringBuilder sb = new StringBuilder(); 61 |         num = Math.abs(num); 62 |         while (num >= radix) { 63 |             sb.insert(0, chars[num % radix]); 64 |             num /= radix; 65 |         } 66 |         sb.insert(0, chars[num]); 67 |         System.out.println((negative ? "-" : "") + sb.toString()); 68 |     } 69 | } 70 | 71 | // Solution 2: 调用 Native Method 72 | import java.math.BigInteger; 73 | import java.util.Scanner; 74 | 75 | public class Main { 76 | public static void main(String[] args) { 77 | Scanner sc = new Scanner(System.in); 78 | String m = sc.next(); 79 | int n = sc.nextInt(); 80 | BigInteger bi = new BigInteger(m,10); 81 | System.out.println(bi.toString(n).toUpperCase()); 82 | } 83 | } 84 | 85 | 86 | /* 87 | 扩张问题 2:M进制 转 N进制 88 | 89 | 将M进制的数X转换为N进制的数输出。 90 | 91 | 输入描述: 92 | 输入的第一行包括两个整数:M和N(2<=M,N<=36)。 93 | 下面的一行输入一个数X,X是M进制的数,现在要求你将M进制的数X转换成N进制的数输出。 94 | 95 | 输出描述: 96 | 输出X的N进制表示的数。 97 | 输入时字母部分为大写,输出时为小写,并且有大数据。 98 | 99 | 示例 100 | 输入 101 | 16 10 102 | F 103 | 104 | 输出 105 | 15 106 | */ 107 | 108 | /* 109 | 直接使用了BigInteger类型,因为BigInteger有这样的数据结构: 110 | BigInteger(String val, int radix) 111 | 将指定基数的 BigInteger 的字符串表示形式转换为 BigInteger。 112 | 113 | toString方法: 114 | toString(int radix) 115 | 返回此 BigInteger 的给定基数的字符串表示形式。 116 | 再根据题目要求,把string变成小写就好了。 117 | */ 118 | 119 | import java.util.Scanner; 120 | import java.math.BigInteger; 121 | 122 | public class Main { 123 | public static void main(String[] args) { 124 | change(); 125 | } 126 | 127 | public static void change() { 128 | Scanner sc = new Scanner(System.in); 129 | while (sc.hasNext()) { 130 | int M = sc.nextInt(); 131 | int N = sc.nextInt(); 132 | String val = sc.next(); 133 | BigInteger big = new BigInteger(val, M); 134 | String str = big.toString(N).toLowerCase(); 135 | System.out.println(str); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /需要排序的最短子数组长度.java: -------------------------------------------------------------------------------- 1 | 该题在左神课程的 2.16排序(4)案例6 中提过. 2 | 值得学习的一种方法 3 | 4 | /* 5 | 给定一个无序数组arr。如果想要让所有元素从小到大排列,求出需要排序的最短子数组长度。 6 | 例如: arr = {1,5,3,4,2,6,7} 返回4,因为只有{5,3,4,2}需要排序。 7 | 注:本题请尽量做到时间复杂度O(N),额外空间复杂度O(1) 8 | */ 9 | 10 | public class Solution { 11 | /** 12 | * 求需要排序的最短子数组长度 13 | * 输入:数组arr 14 | * 返回:需要排序的最短子数组长度 15 | */ 16 | public int getMinSortLength(int[] arr) { 17 | if (arr == null || arr.length == 0) { 18 | return 0; 19 | } 20 | 21 | // 假定最大值,最小值 22 | int max = arr[0]; 23 | int min = arr[arr.length - 1]; 24 | // right 和 left 之间的范围便是需要排序的最短子数组(一开始假定全部范围) 25 | // left 代表从左向右遍历的index; right 代表从右向左遍历的index. 26 | int left = 0, right = arr.length - 1; 27 | /** 28 | * 从右向左遍历,找出需要排序数的最右范围 29 | * (遍历过部分的最大值大于当前正在遍历的值,那么当前值就是invalid,那么真实排序之后,这个最大值在当前位置,或者是更右的位置) 30 | * 只记录发生这种情况的最右位置 31 | */ 32 | for(int i = 1; i < arr.length; i++){ 33 | if(max > arr[i]) { 34 | left = i; 35 | } 36 | else { 37 | max = arr[i]; 38 | } 39 | } 40 | 41 | /** 42 | * 从左向右遍历,找出需要排序数的最左范围 43 | * (遍历过部分的最小值小于当前正在遍历的值,那么当前值就是invalid,那么真实排序之后,这个最小值在当前位置,或者是更左的位置) 44 | * 只记录发生这种情况的最左位置 45 | */ 46 | for(int i = arr.length-2; i > -1; i--){ 47 | if(min < arr[i]) { 48 | right = i; 49 | } 50 | else { 51 | min = arr[i]; 52 | } 53 | } 54 | 55 | if(left == 0 && right == arr.length - 1){ 56 | return 0; // 证明原数组是有序的 57 | } 58 | return left - right + 1; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /魔法币.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cherryljr/NowCoder/85946181d254ea59865a8ba9e22edb88d8777be0/魔法币.java -------------------------------------------------------------------------------- /鹰蛋问题.java: -------------------------------------------------------------------------------- 1 | 鹰蛋问题之前,我们可以来分析一下更为简单的google面试题: 2 | 200 层楼,扔两个鸡蛋求最坏情况下的最小代价。(详见下) 3 | 4 | 数学分析法: 5 | 碎子树:扔碎后需要扔第二枚的最坏情况 6 | 不碎子树:没扔碎,继续扔第一枚的最坏情况 7 | 目的是:找一个最矮的决策树。 8 | 使两子树中高度最大者尽量小, 我们的选择应当使两子树高度尽量接近. 9 | 最终希望的结果是, 整个二叉树尽量像一个满二叉树. 10 | 11 | 假设第一次在根节点上, 我们选择扔 k 层, 那么“碎子树”的高度显然是 k-1. 12 | 为了考虑不碎子树的高度, 设不碎后第二次扔 m 层(显然m>k), 13 | 则这个新节点的碎子树高度为 m-k-1, 不碎子树高度仍然未知, 但按照满二叉树的目标, 14 | 我们认为它与碎子树相同或少1就好. 那么在根节点上的不碎子树的高度就是 m-k-1+1, 令它与碎子树高度相同, 于是: 15 | m-k-1+1 = k-1 => m = k+k-1 16 | 17 | 也即, 如果第一次扔在 k 层, 第二次应该 高k-1 层, 这可以有直观一点的理解: 18 | 每扔一次, 就要更保守一些, 所以让区间长度少1. [1,k) => [k+1,2k-1). 19 | 用类似刚才的分析, 可以继续得到, 下一次应该增高 k-2, 再下一次应该增高 k-3. 考虑: 20 | k+(k-1)+...+1 = k(k+1)/2 = 200 => k≈20 21 | 22 | 知识补充: 23 | https://www.zhihu.com/question/19690210 24 | 25 | DP 解法: 26 | m 表示层数:[1, n] 27 | i 表示蛋的颗数 28 | 1. 若蛋碎了,则只能用 i-1 颗蛋在下面 m-1 层来确定高度,并且要求最坏情况下的次数最少。 29 | 即 f[i - 1, m - 1],总次数为 f[i - 1, m - 1] + 1 30 | 2. 若蛋没碎,这还能继续用这颗蛋在上面的 n-m 层来确定高度。而这其实可以看作是 1~n-m 层 31 | 求最坏情况下的最小代价。总次数为 f[i, n-m] + 1 32 | 题目要求在最坏情况下取最小代价,故应该在这两种情况中取最大者,并且要求在所有决策中取最小值。 33 | ans = min([max([f(i - 1, m - 1), f(i, n-m)]) + 1 34 | 35 | /* 36 | 一幢 200 层的大楼,给你两个鸡蛋. 如果在第 n 层扔下鸡蛋,鸡蛋不碎,那么从前 n-1 层扔鸡蛋都不碎. 37 | 这两只鸡蛋一模一样,不碎的话可以扔无数次. 已知鸡蛋在0层扔不会碎. 38 | 提出一个策略, 要保证能测出鸡蛋恰好会碎的楼层, 并使此策略在最坏情况下所扔次数最少. 39 | */ 40 | 41 | // Solution 1: Mathematical Method 42 | public class Solution { 43 | public int dropEggs(int n) { 44 | long ans = 0; 45 | for (int i = 1; ; ++i) { 46 | ans += (long)i; 47 | if (ans >= (long)n) 48 | return i; 49 | } 50 | } 51 | } 52 | 53 | // Solution 2: DP 54 | 55 | 接下来我们再来看看鹰蛋问题,其实质与扔鸡蛋问题是一样的, 56 | 只是蛋的个数由原来的 2 变成了 k. 57 | 状态转移方程分析见上面的 DP 解法分析部分。 58 | 59 | 鹰蛋问题 5 重优化: 60 | http://www.doc88.com/p-4744136032917.html 61 | 62 | /* 63 | 有一堆共 M 个鹰蛋,一位教授想研究这些鹰蛋的坚硬度 E。 64 | 他是通过不断从一幢 N 层的楼上向下扔鹰蛋来确定 E 的。 65 | 当鹰蛋从第 E 层楼及以下楼层落下时是不会碎的,但从第(E+1)层楼及以上楼层向下落时会摔碎。 66 | 如果鹰蛋未摔碎,还可以继续使用;但如果鹰蛋全碎了却仍未确定 E,这显然是一个失败的实验。教授希望实验是成功的。 67 | 68 | 例如:若鹰蛋从第 1 层楼落下即摔碎,E=0;若鹰蛋从第 N 层楼落下仍未碎,E=N。 69 | 这里假设所有的鹰蛋都具有相同的坚硬度。给定鹰蛋个数 M 与楼层数 N。 70 | 要求最坏情况下确定 E 所需要的最少次数。 71 | 72 | 样例: 73 | M = 1, N = 10 74 | M = 2, N = 100 75 | 76 | 输出: 77 | ANS = 10 78 | ANS = 14 79 | 80 | */ 81 | 82 | class Solution { 83 | /** 84 | * @param m 蛋的数目 85 | * @param n 楼层的高度 86 | * @return 最坏情况下的最小代价(扔多少次) 87 | */ 88 | public int dropEggs(int m, int n) { 89 | if (n == 0) { 90 | return 0; 91 | } 92 | 93 | int ans = 0; 94 | int[][] dp = new int[m+1][n+1]; 95 | for (int i = 1; i <= n; ++i) { 96 | dp[1][i] = i; 97 | } 98 | 99 | for(int i = 2; i <= m; i++) { 100 | for (int j = 1; j <= n; j++) { 101 | dp[i][j] = dp[i][j-1] + 1; 102 | for(int k = 1; k < j; ++k) { 103 | dp[i][j] = Math.min(dp[i][j], 104 | Math.max(dp[i-1][k-1], dp[i][j-k]) + 1); 105 | } 106 | } 107 | } 108 | 109 | return dp[m][n]; 110 | } 111 | } 112 | --------------------------------------------------------------------------------