
数学
abant2
世界上最菜的arcem
展开
-
leetcode 2217 O(1)时间找长度l,第i个回文数
此题给定了时间范围后,可以退化为题目所述的问题,即在 O(1)O(1)O(1)时间找到长度 lll,第 iii 个回文数。解决这个问题,我们需要从回文数的性质入手,即前后是对称的,如果是奇数的话,就多看一位,对折前面几位,还是比较简单的。还有就是快速幂,typedef long long ll;class Solution {public: ll power(ll a, ll n) { if(n == 0)return 1; ll res = pow.原创 2022-04-01 23:21:14 · 214 阅读 · 0 评论 -
leetcode 2197 用栈模拟质数合并
此题并不需要什么算法,模拟即可。但是复杂的模拟方式可能带来很长很丑的代码。此题说了,任意顺序合并都可以得到相同的结果,所以此题可以用单调栈的思想解决,从左边开始,每次弹出两个,不断合并,如果右边可以合并完成,一定会反馈给左边,还是很玄妙的。class Solution {public: int gcd(int a, int b) { if(b == 0)return a; return gcd(b, a%b); } vector<.原创 2022-03-12 17:18:15 · 7825 阅读 · 0 评论 -
leetcode 2195 思路转化
此题看起来比较简单,但最卡人的是这个数据范围,K最多是1e9代表我们无法枚举K,但可以换一种思路,即我们一定是取1-K这K个数的,所以前面出现了哪个,我们就将边界+1,同时把前面的算好即可。由于数组中的重复数没有用,我们排序后与上一个比较去重即可。累加时,用分段累加就可,保证不会溢出。typedef long long ll;class Solution {public: long long minimalKSum(vector<int>& nums, int k) .原创 2022-03-12 15:34:39 · 213 阅读 · 0 评论 -
leetcode 29 两数相除 二进制表示问题
此题主要要解决的是如何用减法模拟除法,如果一个一个减,是一定会超时的,效率也特别低。我们利用多重背包中的知识,即一个数可以由他的多个二进制组合表示。所以,我们从刚好小于他的二进制数开始减即可,如果不够了就让减数除以2,这样能实现log级别复杂度的算法。typedef long long ll;class Solution {public: int divide(int dividend, int divisor) { int sign = 1; if(div.原创 2021-10-24 21:17:17 · 184 阅读 · 0 评论 -
leetcode 1969 快速幂进阶
此题是很变态的一个题目,咱们考虑所有可能性,其实最好的情况就是把所有的变成1,剩下的变成最大值-1,然后做乘积是最大的。所以这就是一个求幂的问题,答案为最大值 val∗(val−1)pval*(val-1)^pval∗(val−1)p,现在就要确定一下这个p到底是多少,其实也很好办,就是减掉1和最大值之后,剩下的平分,即 (val−2)>>1(val-2)>>1(val−2)>>1。然后就是求幂的问题了,由于我们要获取最大值,即2的60次方,是不超过long lon.原创 2021-08-15 19:16:52 · 151 阅读 · 0 评论 -
leetcode 279 完全平方数 背包问题
此题一看就属于没法做那种,和丑数还是不太一样的。丑数要用质因数,此题就是可以转化为背包来做。考虑dp[i]是i的最少数量,dp[i]可以由dp[i-j*j]得到。也和最长上升子序列有些类似。class Solution {public: int numSquares(int n) { vector<int> dp(n+1, 1e5); dp[0] = 0; for(int i=1;i<=n;i++) { .原创 2021-08-03 15:10:51 · 125 阅读 · 0 评论 -
剑指offer 49 找第n个丑数
这个题之前在热题做过了,不过题就是要多做嘛。此题我们可以先用数学的方法尝试一下排序:1,2,3,5,4,6,10上述排序是比较直观的,类似于队列的形式。每次排出一部所有,然后都乘2,3,5. 但是从上述例子就可以看出是不对的,因为4应该在5前面。由于丑数是累乘得到的,我们可以用3个指针分别存2,3,5指向的位置,因为每个数都要乘所有之前的丑数。每次通过循环取最小的即可。去重:由于可能有重复丑数出现,我们每次要判断三个指针有没有指向重复的元素,有的话,就都向前移动指针。比map去重高效很多。ty.原创 2021-08-03 13:03:38 · 112 阅读 · 0 评论 -
leetcode 169 多数元素 抵消法
这种简单题一般都很烦,这个题也是,由于这里面的元素一定大于等于数组的一半,所以可以两两抵消,剩下的那个数就是答案。维护两个变量,count和ans,ans存答案,如果count为0并且又有新的不相等的数就换。class Solution {public: int majorityElement(vector<int>& nums) { int ct = 1, ans = nums[0], n = nums.size(); for(int i.原创 2021-07-29 15:41:20 · 239 阅读 · 0 评论 -
leetcode 287 寻找重复数 类似环形链表
这个题是比较难的一个题,题目要求不允许修改数组,而且常数空间。这里我们就必须用到数组的1到n规定了。注:此题和剑指offer 03很像,但是剑指offer那个题不交换基本是没法做的,此题有不修改数组的解法。此题由于只有一个重复元素,可以使用类似链表的做法,此题其实和环形链表2是一样的,将本身数字当做索引就能一直跳转,由于有重复数字,所以一定能找到环,我们要做的就是找到入环点。代码如下:class Solution {public: int findDuplicate(vector<i.原创 2021-07-28 22:50:54 · 122 阅读 · 0 评论 -
leetcode 142 环形链表2 找环入口 较难数学题
我们使用快慢指针来解决这个问题。设环前路径为a,环长度为b首先,尝试理解第一件事,当两个指针第一次相遇时,慢指针走过的路程是nb。上述结论是可以推导的,设f,s分别为快,慢指针走过的路径,让二者同时出发,f = 2s,并且第一次相遇时 f = s + nb,联立可得出第一个结论、接着来想第二件事,慢指针走多少步才能到入口点?答案是a+nb。第一次相遇时,慢指针已经走了nb了,所以再走a步即可。如何判断走了a步?让一个新指针和慢指针一起走,相遇即可。上述方法不仅能求出入环点,还能得到a的大小。/*.原创 2021-07-28 00:42:31 · 113 阅读 · 0 评论 -
leetcode 96 不同的二叉搜索树 dp+数学推导
此题也和二叉搜索树相关,但是又和排列组合有点关系,是挺难想的一个题目。二叉搜索树这个点应该是最先该入手的点。假设n个节点可能性是 dp[n]dp[n]dp[n],假设以节点i为根节点的可能性是 f(n)f(n)f(n),那么dp[n]=f(1)+f(2)+...+f(n)dp[n] = f(1)+f(2)+...+f(n)dp[n]=f(1)+f(2)+...+f(n)对于 f(i)f(i)f(i),由于加了二叉搜索树的限制,左子树和右子树包含的节点数可以完全确定,即f(i)=dp[i−1]∗dp[n.原创 2021-07-22 21:32:08 · 131 阅读 · 0 评论 -
leetcode 477 汉明距离总和
汉明距离很好求,但是这个题如果用汉明距离暴力做肯定是不行的,会超时。所以此题其实要换一个角度,用数学方法来做。因为一共有32位,假设有一位有x个1,那么这一位上的汉明距离总和就是x(n-x),按照这个思路,我们就可以求得结果,将位作为外循环比较好做。class Solution {public: int totalHammingDistance(vector<int>& nums) { int ans = 0, tmp = 0, n = nums.size.原创 2021-07-17 16:49:26 · 95 阅读 · 0 评论 -
leetcode 461 汉明距离 位运算
此题很简单,异或之后求1的个数就行。解法一:移位求1的个数每次判断最后一位是否为1,然后移位即可。解法二:n&n-1这个做法只需看1有几个即可,因为n&n-1可以消去最后一个1,当为0时停止。class Solution {public: int hammingDistance(int x, int y) { int ret = x^y, ans = 0; while(ret > 0) { a.原创 2021-07-17 16:16:40 · 92 阅读 · 0 评论 -
leetcode 1610 极角序+双指针+环形问题
此题算是一个比较明显的双指针题了,而且是确定最大窗口的那种,只要一个if的那种。困难的是对二维空间的建模,这里用atan2进行极角排序,从-180度开始,每转一圈加360度。对于环状问题,让数组等于两倍自己即可。typedef vector<int> V;class Solution {V start;public: double get_arc(vector<double> v) { return atan2(v[2]-start[1], .原创 2021-07-09 18:57:17 · 123 阅读 · 0 评论 -
leetcode 1915 前缀和历史+状态压缩+异或
这个题一看就属于完全没法做的题,但经过状态压缩后,居然是前缀和历史,醉了。。此题的核心在于建模,如果我们有两个前缀和,能否通过类似减法得到相减后的表示?答案是可以的,定义每个串的状态为10位,1表示这个字母是奇数次,0表示这个字母是偶数次。所以0肯定是满足条件的。如果想合并两个前缀和,只需要用异或即可。这样前面的次数就会被低效。做法:边求前缀和边存历史,和leetcode 560类似。但此题还有一个额外条件,即允许有一个奇数次的。我们只需对每一位进行异或枚举,然后看是否在历史里即可。typedef .原创 2021-07-08 22:19:15 · 252 阅读 · 0 评论 -
leetcode 1486 异或数学技巧
技巧1:对于 f(k) = 1 ^ 2 ^ … ^ k ,可以在O(1)时间方便的求出。这里可以看图上的规律,这个函数是以4为规律的f(k)={kk%4==01k%4==1k+1k%4==20k%4==3f(k)=\begin{cases}k& k\%4 == 0\\1& k\%4 == 1\\k+1& k\%4 == 2\\0& k\%4 == 3\\\end{cases}f(k)=⎩⎪⎪⎪⎨⎪⎪⎪⎧k1k+10k%4==0k%4==1k%4==2k%原创 2021-05-21 23:12:34 · 109 阅读 · 0 评论 -
leetcode 1720 异或逆操作
此题可以简化为,已知a ^ b = c,已知a和c,如何求b?这个问题就是异或的逆操作,我们可以直接求b = a ^ c,这就是异或的逆操作,是可行的。原理:c是ab操作后的结果,位相同取0,位不同取1,可以按位分情况考虑,因为情况很少。如果1和0,结果是1,再和1和0比较,必然是0和1,满足。代码如下:class Solution {public: vector<int> decode(vector<int>& encoded, int first)原创 2021-05-21 11:54:32 · 1069 阅读 · 0 评论 -
leetcode 7 整数反转
解法一(错误):一开始的思路是,我把数先用二进制压缩,让他不越界。但事实上并不能压缩。比如47 > 51,因为这已经超二进制了,如果想压缩必须用质数。下面是代码,可惜错误的思路。class Solution {public: int to_binary(int x) { int ret = 0; for(int base=1;x!=0;base*=2) { ret += (x%10)*base;原创 2021-05-03 16:14:30 · 99 阅读 · 0 评论 -
leetcode 1840 两次遍历消除局部限制
此题类似于135题,但要稍微绕点弯子。此题需要考虑到,每个楼的高度限制实际是对周围的限制加了限制,理解到这个咱们就可以想到,用两次遍历解决这个问题。此外,楼之间最高的可能性可以通过数学方式计算,由于已经消除了不可能的情况,所以肯定能算出来。假设两边为 (i,a)(i,a)(i,a) 和 (j,b)(j,b)(j,b),假设中间最高楼层是ccc,那么可以得到公式(c−a−1)+(c−b−1)=j−i(c-a-1)+(c-b-1)=j-i(c−a−1)+(c−b−1)=j−i即可得到最终答案c=floo原创 2021-05-03 14:23:12 · 398 阅读 · 0 评论 -
leetcode 263 264 313 丑数问题
丑数:质因数只有质数的数问题一:判断一个数是否为丑数直接除就好,因为一个数连乘or连除是logn的,根本不怕。class Solution {public: bool isUgly(int n) { if(n == 0)return false; while(n%2 == 0)n /= 2; while(n%3 == 0)n /= 3; while(n%5 == 0)n /= 5; return n==1;原创 2021-04-29 13:40:42 · 125 阅读 · 0 评论 -
leetcode 1819 子序列gcd个数 枚举数字
本题要根据数据范围入手,枚举子集肯定不可能,gcd比较简单,直接一个一个算就行。本题做法类似于埃氏筛,从1到数组中最大的数,枚举倍数,如果在数组中就求gcd,gcd若和该值相等,说明这个gcd存在。这个逆向思维还是有点意思的。主要考虑了从gcd出发求倍数。class Solution {int ct[200002];public: int gcd(int a, int b) { if(b == 0)return a; return gcd(b, a原创 2021-04-28 22:45:49 · 303 阅读 · 0 评论 -
leetcode 1808 好因子数目 切绳子变形
此题还是比较难转化的,一开始很容易让人看懵逼。题目:给一个质因子数目m,你要选小于等于m个质因子,好因子的定义是能被每个质因子整除,翻译一下就是如果是2,3,3,5,那么他可以只选2,3,5,也可以选2,3,3,5。这样就有两个好因子。所以,这m个质因子要分配多少个相同的,多少个不同的,是个问题。设质数为a,这个数为a1k1∗a2k2∗...∗ajkja_1^{k_1}*a_2^{k_2}*...*a_j^{k_j}a1k1∗a2k2∗...∗ajkj其中,k1+k2+...kj<原创 2021-04-28 22:01:48 · 337 阅读 · 0 评论 -
凸包问题 graham-scan 分治
本题参考数据来自 uva 11626解法一:贪心凸包问题上课讲了两种方法,其中第一种做法是按照极坐标排序+单调栈解决,这种做法比较容易理解。单调栈判断就是看新的点在直线左边还是右边,可以用叉乘的右手法则,即判断x1y2 - x2y1的符号。但有个问题就是当有多个点和初始点连成一条线时,这些点的极坐标顺序不好判断。这是一个很严重的问题,目前还未想到解决方案。此处暂时给出未解决这个问题版本的代码:#include <bits/stdc++.h>typedef long long ll;原创 2021-04-05 14:15:36 · 194 阅读 · 0 评论 -
剑指offer 17 +全排列问题
话不多说,直接手撕一个正常全排序代码。#define _CRT_SECURE_NO_WARNINGS#include <tuple>#include <cstdio>#include <vector>#include <iostream>#include <algorithm>typedef long long ll;using namespace std;int INF = 1 << 30;int N = 3;原创 2021-03-20 18:47:09 · 110 阅读 · 0 评论 -
剑指offer 17 数字,字符串,全排列
此题给定一个N,输出(10, N)之下的所有数字,看到我就会了啊,啪的一下就过了。class Solution {public: vector<int> printNumbers(int n) { vector<int> v; int N = int(pow(10, n)); for(int i=1;i<N;i++)v.push_back(i); return v; }};结果一看题解,发原创 2021-03-20 16:21:54 · 96 阅读 · 0 评论 -
剑指offer 16 实现幂函数
此题一看就是快速幂,但要考虑负数的情况。此处最好的做法是把x变成1/x,同时n取反。这里要注意,由于负数比正数多一个,强行取反可能会越界,要用long long处理一下。同时,零的零次方要注意,这里如果是面试,提前问好这个怎么定义,代码中直接作为0返回了。注意,除2用右移操作会更快。typedef long long ll;class Solution {public: double myPow(double x, int n) { if(n == 0)return 1;原创 2021-03-19 10:55:41 · 105 阅读 · 0 评论 -
最近点对问题 c++
最近点对的算法还是比较清楚的,在实际代码阶段要让效率达到最高,需要先对x排好序,递归过程中保证不再更改(因为x只用于分割这一步)。之后对y的排序要融合归并排序进去,即把元素分割到1个,这样保证排好序。之后在合并步骤只要合并两个排序好的数组,O(N)即可解决。#define _CRT_SECURE_NO_WARNINGS#include <cmath>#include <tuple>#include <cstdio>#include <vector>原创 2021-03-18 15:47:08 · 600 阅读 · 0 评论 -
leetcode 31 下一个排列 贪心(有难度)
这个题还是有难度的,不看题解挺难想。第一个重要的点是要找左边尽量靠右的元素和右边尽量小的元素交换,交换后对左边位置后面的元素排序/反转。这个是要自己找规律(很难找)第二个点就是,已知上面的算法,如何找到这两个点。这个方法就更妙了。首先要找尽量靠右的元素,尽量靠右,但要保证他右边还有比他更小的元素。所以我们要从右往左找,直至找到一个元素使得nums[i-1] < nums[i]这样就找到左元素。此时可以发现右边是一个降序序列,只要从右往左找,找到第一个比左元素大的元素就是右元素了(能满足尽量原创 2020-11-10 10:21:34 · 137 阅读 · 0 评论 -
leetcode 204 经典素数O(N)线性筛
解法一:暴力 O(NN\sqrt{N}N)看到一个数x,遍历[2, x]看是否整除,是最暴力的。此题还有个稍优雅解法,即遍历[2, x\sqrt{x}x]。由于一个数如果能被整除,那一定还有另一个数作为商,而这两个不可能都大于x\sqrt{x}x。直接这样做也会超时,可以只遍历奇数,并且保存存过的素数,只检查是否被素数整除即可,能勉强通过。from math import sqrtclass Solution: def countPrimes(self, n: int) -> in原创 2020-12-03 12:30:01 · 163 阅读 · 0 评论 -
leetcode 62 组合数+动态规划
解法一:组合数一共有m-1和n-1种向下或向右的走法,用组合数排序from math import factorialclass Solution: def uniquePaths(self, m: int, n: int) -> int: return int(factorial(m+n-2)/(factorial(m-1)*factorial(n-1)))解法二:dp和爬楼梯问题类似,解法求和,啪的一下就过了class Solution: def un原创 2020-12-09 12:43:37 · 202 阅读 · 1 评论 -
ZJ2 足球 数学解方程
此题真的是做的我难受的很。题目给出了三个队伍的差,但没说是正还是负,咱们的目标就是得到这三个数,之后就可以返回结果。实际操作中,并不需要真正解出这三个数,因为情况很多要分类讨论,咱们要满足解出的每个数>0,并且剩下的够分。代码很简洁,但是思路挺乱,最好提前写好注释,以小的为基础列方程。N = int(input())for i in range(N): l = list(map(int, input().split(' '))) n, k, d1, d2 = l[0], l[1],原创 2021-03-10 16:39:24 · 198 阅读 · 0 评论 -
剑指offer 10.1 斐波那契数列
解法一:dp题目都告诉你递归式了,再写不出来怎么说你呢。class Solution: def fib(self, n: int) -> int: MOD = 1000000007 dp = [0, 1] if n < 2: return dp[n] for i in range(2, n+1): dp.append((dp[i-1] + dp[i-2])%MOD) return原创 2021-03-13 14:55:11 · 147 阅读 · 0 评论 -
剑指offer 14 切绳子求最大乘积 快速幂求余
此题要把绳子切成m段,求最大乘积。很直观的就是,要尽量相等的绳子才能有更大的乘积。关键是要切成多长的。从这个题解中,我们可以看到,切成3的就很好。这是从5开始的情况,3-4由于没法切3,或者切3不是最好,要用2切,而2需要用1切,这些特殊情况处理好就没问题了。值得注意的是,e真是个神奇的数字。...原创 2021-03-17 13:52:06 · 172 阅读 · 0 评论