做算法题各种题类与形式上的总结(不断更新)

(1)枚举

总结:

①dfs的时候考虑一下需不需要维护k(上一个for循环的值=》题目提到不考虑先后顺序的时候=》代表先后顺序算同一种)。

②当旋转镜像算同一种,可以看原图形旋转镜像后有几种可能x,在枚举的时候不用管,枚举完得到res,最后用res/x

常见题目与解题思路(时间复杂度):

分糖果:横向:for两种不同糖果,竖向:学生人数。维护一个hashmap,key是学生索引,value是一个templist。横向的时候当a和b符合题意则将该学生与糖果存入hashmap,当到达截至点则取出所有学生的a和b,验证a是9个,b是16个则count++

跳跃:维护next数组(包含各个方向)终止条件:y和x到达题目的位置,然后Math.max。横线:for next数组实现方向变化。

灌溉:维护一个vis[][]表示被访问的点。每个水管点都进行dfs,终止条件:时间,横向:四个不同方向。访问过的点设为1,最后计算有几个1的点。

特殊日期:维护两个数组,分别表示闰年和平年不同月份的天数

竖向:年的位数,当大于3则代表有四个位数,进入日期判断。横向:年每位的数字(0-9题目要求的1900-9999年)

由于年份没有太大规定,而不同月的天数会因为年而不同,因此枚举年。

在判断中:双层for,第一层for月份再进行闰平年判断(因为闰平年都有12个月),第二层for天数,通过第一层的月份,获得月份和天数后加起来年加起来进行判断,成功则count++

01串的熵:假设0有x个,1就有len-x个,x<len-x,所以x满足x<len/2,因此枚举x为0到len/2,计算每次res,当在答案中则输出。有两点想要分享,一个是要注意题目给的是四舍五入结果,一个是log算法。

***硬币兑换(填空题):两层for循环,一层指向一个硬币(i=1到2023),一层指向另一个硬币(j=i到2023),枚举不同硬币组合可以变成的值,最后求出最多的值。另一个思路:枚举一个面值硬币的各种兑换方式,例如第一层for要枚举的面值b,第二层要for(1到b/2),第三层要for(b/2+1到b-1)

**回文日期:维护两层for,第一层遍历月份,第二层是月份对应的天数,0229的回文日期是92200229,但是9220是平年,因此没有0229这个日期,所以所有年的2月都由28天即可。

  int[] m= {0,31,29,31,30,31,30,31,31,30,31,30,31};

通过两层遍历得到所有日期对应的回文日期,判断如果有这一天且在题目规定的范围内则count++。

***金额查错:通过题目给的错的金额与账单总数,得到欠的钱t。直接dfs枚举,终止条件:t<0return,t==0则进行输出。横向:for账单,但是得由上一栈传来的start开始,不过由于题目要求不需要重复输出(例如还欠t=7,账单包含334,但是只需要输出一次34),因此一个位置上只能出现一次同样的数字(当一个位置遍历了第二个数开始,且下一个数与上一个数一样则continue)

 for(int i=start;i<all.length;i++){
                if(i>start&&all[i]==all[i-1]){
                    continue;
                }
}

牌型种数:总共有13个位置,由于不考虑牌的先后顺序,所以dfs的时候需要传入一个参数k,来表示上一个位置放了哪个数,后面的不能再放小于k的数,就不会重复得到同种数量类型的组合牌型

(2)递推

总结:

常见题目与解题思路(时间复杂度):

耐摔指数:f1[x]、2、3分别表示有一二三部手机的时候,需要测x层最差运气下最佳策略所需要的次数。至于这个最佳策略与最差运气的理解,就是枚举从不同层开始摔下需要多少次,且由于要求运气不好,所以每次都要朝多摔的方面,例如现在测试f2[j]的值,假设先从第k层开始摔,得到运气最差摔得最多的MAX(f2[k-1],f1[j-k]),然后枚举所有不同k层开始摔的max统计起来,求Min才能得到最佳策略。f1[x]=x(必须从0层开始,才有可能测出来,不然中间直接碎了就没有数据了,最坏情况就是顶层也不碎)。对于f2[j],维护双层循环jk,j表示有几层的情况下,k表示从第k层(范围1-j)开始,没碎则f2[j-k](因为没碎就有两个手机,j-k层需要测试),碎了则f1[k-1]。对于f3[j],遍历j层,测试从k=1层开始,没碎则f3[x-k],碎了则f2[k-1]。

(3)贪心

总结:

贪心算法的思想是每一步选择当前状态下最优的决策,并不考虑之后的结果,以期达到全局最优解的算法思想。大部分会用到sort方法。贪心策略把一个大问题看成每个小问题,每个小问题都用最优解解决,而且方法一样,并且这个方法可以用在每个小问题上,所有小问题的解组成成原问题的解。具有贪心性质的问题,贪心算法通常能够得到比较优秀的近似解。而且,贪心算法的时间复杂度都比较低。例如有些题目的方法是每次都找最大或者最小的值,中间可能有限制停止的条件,最后组成答案。问题问的某个问题,可以把这个问题里的大单位拆成一个个小单位,每个小单位可以看题意取最大或者最小最后组成结果看看是不是答案,如果是这个方案大概就是贪心方案,先用题目中的例子缩小某单位最大或者最小的可能性,然后自己举例子再找规律

常见题目与解题思路(时间复杂度):

付账问题:排序每个人有的钱。遍历循环,如果这个人所有的钱*后面的人<sum,则这个人付他全部的钱,如果>sum,则所有人平均给这些钱(题目中要求偏差小  =》 不够的全给,够的平均给剩余要a的钱)

删除字符:从删除字符转换为挑选字符,使最小。要求最小则尽可能让前面的字符小,遍历原字符,提取的每个字符在规定的一个区间内,使取的每个字符都尽可能小(题目中要求最小的单词=》前面的字符越小越好)

答疑:通过自己举例子,可以发现某个同学花的时间越小,则让他最先提问,总能得到题目要的最优解。因此排序每个同学花的时间,再遍历sum即可。(贪心性质,让每个提问的同学时间都最小,让最小的先提问,都是使用一个方法最后得到最优解)

管道:二分所需要的时间,再把时间代入方法。在方法中,维护一个index,由于管道是按顺序输入,当每个管道往前可以勾到index,则让index走到管道后面的路径,否则continue不能break。例如前面管道开启的时间比后面管道开启时间晚的多,那么后面管道也可以勾到前面管道勾不到的长度,然后让index衍生到下一个管道后面的长度,更新index。最后判断index是否>=len。(题目中要求最早时间。使用二分时间的原因是当一个x时间可以成功,则x后面的时间一定可以成功,前面的时间不一定,则可以让right=mid,否则left=mid+1,就可以找到最短时间)

最大开支:维护一个ArrayList数组,遍历每个项目,当该项目增加了人数比上一次人数的总价要多则往数组中增加多出的费用,遍历结束后,数组的长度代表了有多少人可以让所有项目里每个项目都总价格最贵,数组里的每个数据表示多出的余额。然后在数组长度和题目给出的人数取最小值。如果数组长度更小,则多出的人不配玩。如果数组长度更大,则从多出的余额开始从大到小遍历,遍历不上的数据都是多出的余额更小的。(感觉有两次贪心,第一次是让所有项目都实现总价格最高,第二次是让多出余额更多的入选,多出余额小的则只是人数不够)

三国游戏:维护三个数组对应不同国家获胜。从大到小排序,每个国家进行sum增加,直到<0代表开始失败,看哪个国家sum的数值最大

填充:类似字符串索引前后对比题,使用遍历,i先与前面比较,再与后面比较。维护一个vis数组代表该索引是否被匹配。

买二增一:排序所有商品价格,从后开始遍历,使得赠送的商品价格走大(贪心),维护vis数组表示i是否被赠送了。可以使用二分找到可以被赠送的价格大的商品(<x的最大值)。

翻硬币:遍历索引即可,只和后面的进行翻转,因为前面的已经匹配,感觉不到贪心

平均:每个数出现的次数固定,如果某个数出现的次数过多,则挑出多的最小进行累加就行(贪心性质),不必关心该数变成什么。

(4)搜索

总结:

DFS不保证找到的是最短路径,如果需要找到最短路径,通常会使用取最小值或者宽度优先搜索(Breadth-First Search,简称BFS)或Dijkstra算法等其他算法。

一般dfs题目也有分两种

①走格子模式,step作为竖型递归,横向for一般是走的方向。

②类似枚举dfs,根据题目要的答案的属性,一般有ab两种属性,有可能的方法是a属性递归,b属性横向for,或者反过来,也有可能类似排序小球题目一样,ab都是横向for,而递归是总数量,当总数量到达一个值时截至(累加/Math.min)

一般题目可能可以dfs,但是不能bfs,类似剪格子,里面的点有一些需要被不同点访问多次,但是bfs不具备回溯的功能,因此不适用。

常见题目与解题思路(时间复杂度):

受伤的皇后:纵向是皇后放的行,横向for是列。当达到了行限制,则对数组是否合法来进行sum。

排序小球:纵向:剩余球的数量 横向:球的颜色和球对应的颗数。遍历颜色再数量,因为颜色决定数量。当剩余数量为0,则代表全部排完进行累加count,(题目中要求颜色不一样且数量递增 =》 dfs中需要维护上一个放的颜色和球数)

分考场:维护一个list<List<Integer>>,list.size的长度代表课室数量,一个vis保存不能在一个考场(类似双向图)。竖向:学生下标索引,当索引超过学生数量则进行Math.min,剪枝当考场数量>res,则不再需要dfs。横向 :遍历已有的课室,如果该学生可以进入之前的考场则加入再dfs下一位学生,回溯要删除list.get(i)的最后一位,如果学生不可能进入之前的考场则重新创建一个templist存入双重list中。

穿越雷区:可以使用bfs(维护一个队列和next数组和vis数组,先将首点存入队列,然后设为已访问,四个方向不可走则continue,可走走放入队列)。也可以使用dfs(维护res保存最小值,next数组。当访问的点是终点则进行Math.min。先将点设为已访问,然后访问四个方向,访问结束了再将该点设为未访问)。

剪格子:维护一个ints[][]存储图,还有一个next数组存储走的位置。dfs竖向:走的每一步step,走的数的总数sum,当sum等于题目要求,则Math.min,剪枝:当加起来的数大于题目要求或者step>res则return。横向:将点进行访问,然后借用next数组访问各个点,最后回溯该点。(这题无法使用bfs,因为bfs没有回溯功能,会导致本来应该走的点,本来应该加的值被其他点访问过,如果不引入vis数组,就会无限循环。bfs不能用的题型之一:需要回溯,一个点可以被其他不同点多次访问)

(5)回溯

总结:

常见题目与解题思路(时间复杂度):

(6)动态规划

总结:

动态规划通常用于具有重叠子问题最优子结构的问题,其中重叠子问题指的是在解决问题的过程中,需要反复计算的子问题子问题也需要前一个子问题的答案。最优子结构则是指问题的最优解可以由其子问题的最优解组合而成把问题分解为多个小问题,每个小问题的最优解组合成原问题的解。与贪心的说法有点相似,但是每个小问题的解它使用的方法不一样,例如动态规划有Math.max的方式,这里是与贪心不一样的地方。

常见题目与解题思路(时间复杂度):

普通dp:

跳跃:dp[i][j]代表当到达ij这个点,最大的权限值。从头开始遍历,四层for循环,ij循环是要设置值的点坐标,i对应走的路k,j对应走的路l。j-l+i-k<=3步。dp[i][j]=Math.max(dp[i][j],dp[k][l]+num[i][j]);

*****摆动序列:当i为奇数时,dp[i][j]表示第 i 个数选择大于等于数 j 的方案数。当i为偶数时,dp[i][j]表示第 i 个数选择小于等于数 j 的方案数。因为i为奇数的值要比前面的大,i为偶数要比前面的小。

当i为奇数的时候,j从往前面遍历(因为选择大于j的数=i点上是j的数+之前的点大于j+1的数),当i为偶数的时候,j往后面遍历(因为选择小于j的数=i点上是j的数+之前的点小于j-1的数)

来自大师【蓝桥杯】省塞模拟赛 摆动序列(动态规划)_蓝桥杯省赛模拟摆动序列-CSDN博客

***买不到的数目:dp[i]代表数字i是否可以被购买,初始化dp[0]=1,当1表示可以购买,当0表示不能购买。当dp[i-n]或者dp[i-m]为0则代表i是n或者m的倍数,则可以被购买,设为1。由于i从头到尾遍历,所以dp[x]可以由不同倍数的i或者j组成,也可以代表被购买。

for(int i=1;i<n*m;i++){
           if((i-n>=0&&ints[i-n]==1)||(i-m>=0&&ints[i-m]==1)){
               ints[i]=1;
	           }
	        }

状态dp:

蜗牛:dp[i][j]当j为0则走到i点的地上,如果j为1则走带i的天上。dp[i][0]=dp[i-1][0]+x,dp[i-1][1]+x

dp[i][1]=dp[i-1][1]+x,dp[i][0]+x,0由01来,1由01来,不会出现001 011情况(题目中要求最小时间到达 =》 每个索引位置都有天上和地下两种状态,可以让后面i个柱子使用时间最小,不能用贪心是:不能让每个竹子都固定选择天上和天下,每个柱子天上地下的最小时间都与前一个柱子天上地下的时间有关)

保险箱:dp[i][j]当j为0时,索引i是通过不退不进,1为是进位而来,2是退位而来。因为后面进退会影响前面,所以从后面的索引开始遍历,0可以由012来,1也可以由012,2也可以由012,取math.min,但是不可能出现0012 0112 0122的情况(题目中要求最小操作次数 =》 每个索引的数都有不进不退,进或退三种状态,可以让后面i位开始操作次数最小,不能用贪心是:不能让每一位都固定选择三种情况中的一种,每个位置都由于前后的大小而影响)

松散子系列:dp[i][j],当j为0则代表前i索引主不选第i个 ,j为1则代表前i索引要选上第i个。
dp[i][1]只需要dp[i-1][0]+x即可,dp[i][0]则需要在dp[i-1][0]和dp[i-1][1]中取最大值,让前i的价值最大。(题目中说要子系列最大价值 =》 每个索引可以有选与不选的两种状态,可以让前i的价值最大。不能使用贪心的原因是:不能从全部里选字符最大,字符长度不确定,也没办法在一定范围内选最大,因为子串zz小于几百个a)

区间dp(大部分第一层for都是长度,第二层是左边索引):

更小的数:维护dp[i][j]代表字符串的索引i和j互换符合题意。第一层的for是子串的长度i,第二层的j是左边索引,通过i和j计算右边索引r,当左边小于右边则将dp[j][r]设为1,表示j和r翻转符合题意。当小于右边,则dp[j][r]=dp[j+1][r-1],长度变小,所以前面已经遍历过。

(7)分治

总结:

常见题目与解题思路(时间复杂度):

(8)递归:

总结:

常见题目与解题思路(时间复杂度):

饮料换购:计算总共获得的饮料数量,有的饮料还可以换为瓶盖,属于同一个处理方法,递归参数是饮料数量,方法功能 =》参数可以获得的最大的饮料数

(9)数论:

总结:

①同余做差一定是mod的倍数

②ax+by=c,当a与b互为质数,则有特殊值(a*b-a-b),当大于这个值,该方程都有解。当不止a与b的时候,这个特殊值是两个max的值相乘

当a与b互为非质数,则通过费马小定理得到ax+by=(c==gcd(a,b)),c只有是gcd(a,b)的倍数,方程才有解,因此c有无限个值(当c不是gcd倍数)导致方程无解。

常见题目与解题思路(时间复杂度):

垒骰子:原本使用dp,但是数据规模到10^9次方,而且需要遍历i第几层,j该层朝上是数字几,会超时,因此转换为矩阵。具体参考这位大佬蓝桥杯2015年第六届真题-垒骰子-CSDN博客,记录一下(二维矩阵相乘)+(一维矩阵和二维矩阵相乘)的方法

public static long[][] chengfa(long[][] x,long[][] y) {//两个矩阵相乘,具有普适性
	long ans[][]=new long[x.length][y[0].length];
	for(int i=0;i<x.length;i++)
		for(int j=0;j<y[0].length;j++)
			for(int p=0;p<x[0].length;p++ )
				ans[i][j]+=(x[i][p])*(y[p][j])%mod;
	return ans;

 }
public static long[] chengfa1(long[][] x ,int[] y ) {//两个矩阵相乘,具有普适性
	long ans[]=new long[y.length];
	for(int i=0;i<x.length;i++)
		for(int j=0;j<y.length;j++)
		
				ans[i]+=(x[i][j]%mod)*(y[j]%mod)%mod;
	return ans;

}

凑算式:能用乘法就把除法转为乘法,减少误差

k倍区间:将前i项的和取模并赋值为s数组中,s[0]设为0(模后为0),当模后值相等,则s[i]-s[j]为k倍区间(总结一),随机在模后的数量中取2个则可以构成k倍区间,则c上2下m即可

包子凑数(凑不到的数目类型):维护一个cun数组保存输入数据,再提出一个temp变量,用于查看是否互质(总结②),当非互质则输出inf,当互质则维护一个dp,将dp[0]设为1(代表可以倍买到),遍历1-特殊值(总结2),当dp[i-每次可以拿的数]为1,则代表也可以被买到,例如dp[i-2/4/5]==dp[0]==1,则把该i点标记为可以买到了,当dp后面的值是这个数(2/4/5)的倍数都可以被标记到

(10)博弈:

总结:

常见题目与解题思路(时间复杂度):

取球博弈:搭配记忆化存储,对取球数量进行排序。当数量<最小取球数则开始返回。竖向:剩余的球数,我与对手的球数奇偶性。char类型不为空的判断

cache[num][me][you]!='\0'。横向:每次拿球的数目。递归的时候两个人直接参数进行变换。在编写时已自己为第一视角,如果递归给对手为第一视角后返回是输,则我为赢,遍历所有情况都赢不了则为输。当有第三种平局可能性则维护一个boolean,到最后判断boolean是平局就行,如果平局都不行,则为输

巴什博弈:如果没有规定一次能拿{m1.m2.m3}个,只规定1-最大m个,则每次拿剩m+1的倍数个,让对手拿,如果我一开始就遇到是m+1的球数,那我必输,总之,谁拿的时候先遇到m+1的球面,谁输
威佐夫博弈:从两堆中取同样多的物品,面对奇异局势比输,谁先面对非奇异局面谁赢,因为可以把局面变为奇异局面给对手。

直接出结论:若两堆物品的初始值为(x,y),且x < y,则令z=y-x;

w=(int)[((sqrt(5)+1)/2)*z ];

若w=x,则先手必败,否则先手必胜。

int Wgame(int a,int b)
{
 a=min(a,b);
 b=max(a,b);//先交换大小位置
 int k=b-a;
 double path=(sqrt(5.0)+1.0)/2;
 if(a==int(path*k) return 1;
 return 0;
}

其中出现了黄金分割数(1+√5)/2 = 1.618…,因此,由ak,bk组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,bj+1 = aj+1+ j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。

(11)图论:

总结: 

①通过题目参数得到确定的坐标轴x或y,可以尝试在图中找到等差等比数列,引出一个特殊点,便于得到答案的值

常见题目与解题思路(时间复杂度):

螺旋折线:右上角的点,与左下角的点分别是等差,取个折中点右下角的点A,通过题目给的参数得到在第几圈,离A点的距离d,通过等差数列+-d得到答案(点A的长度去题目参数到点A等于题目参数的长度)

全球变暖:遍历图,把为#的进行bfs/dfs,维护mark数组,记录点是否访问过,当访问到#则acount++,当该点四面有一个是海,则将flag设为true,bcount++(该点会被淹没),当acount==bcount代表有x个陆地,且有x个陆地都被淹没,所以ans++  =》 一个岛屿没了

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值