• 「算法刷题宝典」必须知道的知识点和技巧


    c680481fa07e0cf1320c6ab40a1c3c24.png

    点击蓝字 关注我们

    d7fda89c491e1d9c2f9758313dc1a8d5.png

    【算法学与练】如期而至,你准备好了吗?

    上周,我们在算法刷题群发布了「每日一题」,大家都会做吗?今天,我们来汇总一下上周的算法知识点及题目!错过的小伙伴千万别再忘记刷题啦!

    边学边练,yyds!

    跟着我一起学算法吧!欢迎你的加入~

    2d34e63205fbfac0003a096217b8a983.png

    5d99baecace5f1ab2ec4b538ff3d4560.png

    01

    基础算法——枚举、模拟

    枚举算法是我们在日常中使用率最高的一个算法,它的核心思想就是:枚举所有的可能

    枚举法的本质就是从所有候选答案中去搜索正确的解,使用该算法需要满足两个条件:

    • 可预先确定候选答案的数量;

    • 候选答案的范围在求解之前必须有一个确定的集合。

    d8a0f531744ebdb1757020472225084a.png

    (图片来源网络:枚举算法的流程图)

    枚举的一般形式为:

    1. enum 枚举名
    2. { 枚举值表 };

    举个例子来看看:

    1. enum weekday
    2. { sun,mou,tue,wed,thu,fri,sat };

    该枚举名为weekday,枚举值共有7个,即一周中的七天。

    那枚举算法到底好在哪呢?

    枚举的算法简单,在局部地方使用效果极佳。但运算量比较大,如果遇到问题规模变大时,循环的阶数越大,执行速度越

    当然,并不是所有题目都适合枚举,它在使用中有以下规定:

    枚举值是常量,不是变量。

    不能在程序中用赋值语句再对它赋值。

    例如,对枚举 weekday 的元素,再作以下赋值:sun=5;mon=2;sun=mon; 这都是错误的。

    枚举元素本身由系统定义了一个表示序号的数值,从 0 开始顺序定义为 0,1,2……如在 weekday 中,sun 值为 0,mon 值为1, ……sat 值为 6。

    d98e9d68ca8ff8262458efba630b5956.gif

    练习题来啦!

    1、卡片

    cfa23f59af8e26cf0812e994c740f56c.png

    参考代码

    1)C

    1. #include <stdio.h>
    2. int main(void)
    3. {
    4. int i,t,sum=0;
    5. for(i=1;;i++)
    6. {
    7. for(t=i;t!=0;t/=10)
    8. if(t%10==1)
    9. sum++;
    10. if(sum>=2021)
    11. goto end;
    12. }
    13. end:printf("%d",i);
    14. return 0;
    15. }

    2)C++

    1. #include <iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. long int i=0;
    6. long int sum = 2021,tmp=0;
    7. long int b=0;
    8. for(i=1; i<20210; i++) {
    9. //记住需要一个数把i的值给记下来,因为后面的相除那一个数是发生变化
    10. tmp=i;
    11. while(tmp) {
    12. b = tmp%10;
    13. if(b==1 && sum>0) {
    14. sum-=1;
    15. }
    16. tmp/=10;
    17. }
    18. if (sum==0) {
    19. break;
    20. }
    21. }
    22. cout<<i;
    23. return 0;
    24. }

    3)Java

    1. import java.util.Scanner;
    2. // 1:无需package
    3. // 2: 类名必须Main, 不可修改
    4. public class Main {
    5. public static void main(String[] args) {
    6. int count=0;
    7. for (int i = 1; i < 20210; i++) {
    8. String str=i+"";
    9. for (int j = 0; j < str.length(); j++) {
    10. if(str.charAt(j)=='1')
    11. {
    12. count++;
    13. }
    14. }
    15. if(count==2021)
    16. {
    17. System.out.println(str);
    18. break;
    19. }
    20. }
    21. scan.close();
    22. }
    23. }

    4)Python

    1. num=0 #用num用来累计用过的数字“1”的次数
    2. for i in range(1,10000):
    3. num+=str(i).count("1") #出现过几次1就 给num加上多少
    4. if num>2021: #当num数量超过2021时卡牌数量不足
    5. break
    6. print(i-1)

    2、回文日期

    1138ff6a133e67566c90348a60586649.png

    参考代码

    1)C

    1. //分析:是否合法(年月日)-->不合法,跳过
    2. // -->合法--是否是闰年-->分解日期(装入数组)-->判断是否满足回文-->设置变量只打印下一个
    3. #include<stdio.h>
    4. int mon[12]={31,28,31,30,31,30,31,31,30,31,30,31};
    5. int arr[8]={0};
    6. int year,month,day,j,flag=0,temp;
    7. int legal()//判断是否合法
    8. {
    9. if(year%400==0)
    10. mon[1]=29;
    11. else
    12. mon[1]=28;
    13. if(year<1000||year>9999||month<=0||month>12||day>mon[month])
    14. return 0;
    15. return 1;
    16. }
    17. int main()
    18. {
    19. int time;
    20. scanf("%d",&time);
    21. for(int i=time+1;i<=99991231;i++)
    22. {
    23. year=i/10000;
    24. month=i/100%100;
    25. day=i%100;
    26. if(!legal())
    27. continue;
    28. j=0;temp=i;
    29. while(temp)
    30. {
    31. arr[j++]=temp%10;
    32. temp/=10;
    33. }
    34. if(arr[0]==arr[7]&&arr[1]==arr[6]&&arr[2]==arr[5]&&arr[3]==arr[4])
    35. {
    36. if(flag==0)
    37. {
    38. for(int t=0;t<8;t++)
    39. printf("%d",arr[t]);
    40. printf("\n");
    41. flag=1;
    42. }
    43. if(arr[0]==arr[2]&&arr[1]==arr[3])
    44. {
    45. for(int t=0;t<8;t++)
    46. printf("%d",arr[t]);
    47. break;
    48. }
    49. }
    50. }
    51. return 0;
    52. }

    2)C++

    1. #include <iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. // 请在此输入您的代码
    6. int i,n,a,b,c,d,e,f,g,h;
    7. int j=0;
    8. scanf("%d",&n);
    9. for(i=n+1;i<=89991231;i++)
    10. {
    11. a = i%10;
    12. b = (i/10)%10;
    13. c = (i/100)%10;
    14. d = (i/1000)%10;
    15. e = (i/10000)%10;
    16. f = (i/100000)%10;
    17. g = (i/1000000)%10;
    18. h = (i/10000000)%10;
    19. if((a==h)&&(b==g)&&(c==f)&&(d==e))
    20. {
    21. if(j==0)
    22. {
    23. printf("%d\n",i);
    24. j++;
    25. }
    26. }
    27. if((a==h)&&(b==g)&&(c==f)&&(d==e)&&(a==c)&&(b==d))
    28. {
    29. printf("%d",i);
    30. break;
    31. }
    32. }
    33. return 0;
    34. }

    3)Java

    1. import java.util.Scanner;
    2. // 1:无需package
    3. // 2: 类名必须Main, 不可修改
    4. public class Main {
    5. public static void main(String[] args) {
    6. Scanner sc = new Scanner(System.in);
    7. int data = sc.nextInt();
    8. huiwenriqi(data);
    9. ABhuiwenriqi(data);
    10. sc.close();
    11. }
    12. public static void huiwenriqi(int n) {
    13. for (int i = n+1; i <= 89991231; i++) {
    14. String str = i+"";
    15. char[] ch = str.toCharArray();
    16. char a = ch[0], b = ch[1], c = ch[2], d = ch[3],e = ch[4],f = ch[5],g = ch[6],h = ch[7];
    17. if(a==h&&b==g&&c==f&&d==e) {
    18. System.out.println(ch);
    19. break;
    20. }
    21. }
    22. }
    23. public static void ABhuiwenriqi(int n) {
    24. for (int i = n+1; i <= 89991231; i++) {
    25. String str = i+"";
    26. char[] ch = str.toCharArray();
    27. char a = ch[0], b = ch[1], c = ch[2], d = ch[3],e = ch[4],f = ch[5],g = ch[6],h = ch[7];
    28. if(a==c&&c==f&&f==h&&b==d&&d==e&&e==g) {
    29. System.out.println(ch);
    30. break;
    31. }
    32. }
    33. }
    34. }

    4)Python

    1. import datetime #导入日期库
    2. idate=input()
    3. y=int(idate[:4]) #取出输入的年月日
    4. m=int(idate[4:6])
    5. d=int(idate[6:])
    6. dd=datetime.date(y,m,d) #将输入的表示日期的字符串转换成日期
    7. flag=True #回文日期只输出一次
    8. for n in range(9999999):
    9. dd=dd+datetime.timedelta(days=1) #日期不断增加1天
    10. sd=str(dd).replace('-','') #将日期中的-去掉
    11. if sd[:]==sd[::-1]: #判断日期是否是回文
    12. if flag:
    13. print(int(sd)) #输出回文日期
    14. flag=False #下次不输出回文日期
    15. if sd[0]==sd[2]==sd[5]==sd[7] and sd[1]==sd[3]==sd[4]==sd[6]: #判断是否是ABABBABA类型
    16. print(int(sd))
    17. break

    3、赢球票

    ecd07018f92cd0cbbdbd18df18b0855d.png

    参考代码

    1)C

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. int a[102]={0},b[102]={0},N,i,max=0,j,sum=0,k,h,m,max1=0,l;
    4. void dat()
    5. {
    6. for(i=0,sum=0,j=1;j<=max;)
    7. {
    8. if(a[i]<0)
    9. {
    10. i=0;
    11. }
    12. if(a[i]==j)
    13. {
    14. sum=sum+j;
    15. j=1;
    16. for(k=i;a[k+1]!=0;k++)
    17. {
    18. a[k]=a[k+1];
    19. }
    20. continue;
    21. }
    22. i++;j++;
    23. }
    24. if(max1<=sum)
    25. max1=sum;
    26. }
    27. int main(int argc, char *argv[])
    28. {
    29. scanf("%d",&N);
    30. for(i=0;i<N;i++)
    31. {
    32. scanf("%d",&b[i]);
    33. max=max>=b[i]?max:b[i];
    34. }
    35. b[i]=-1;
    36. for(h=0;h<N;h++)
    37. {
    38. m=b[0];
    39. for(i=0;i<N-1;i++)
    40. {
    41. b[i]=b[i+1];
    42. }
    43. b[N-1]=m;
    44. for(l=0;l<N+1;l++)
    45. a[l]=b[l];
    46. dat();
    47. }
    48. printf("%d\n",max1);
    49. return 0;
    50. }

    2)C++

    1. #include <iostream>
    2. #include <algorithm>
    3. #include <string>
    4. #include <cstring>
    5. #include <queue>
    6. #include <utility>
    7. #include <cmath>
    8. #include <unordered_map>
    9. #include <unordered_set>
    10. #include <vector>
    11. #include <deque>
    12. #define io ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
    13. #define rep(i, a, b) for(int i = a; i <= b; i ++)
    14. #define x first
    15. #define y second
    16. using namespace std;
    17. const int N = 110, M = 400010;
    18. typedef long long LL;
    19. typedef pair<LL, LL> PLL;
    20. typedef pair<int, int> PII;
    21. int n, m;
    22. unordered_set<int> s;
    23. int a[N];
    24. int main()
    25. {
    26. io;
    27. int maxn = 0, res = 0;
    28. cin >> n;
    29. rep(i, 0, n - 1)
    30. cin >> a[i], res += a[i];
    31. rep(i, 0, n - 1)
    32. {
    33. s.clear();
    34. int cnt = 1;
    35. int j = i;
    36. int sum = 0;
    37. int k = 0;
    38. while(j != -1)
    39. {
    40. k ++;
    41. if(k > 50000) break;
    42. j = j % n;
    43. //cout << a[j] << " " << cnt;
    44. if(s.find(a[j]) != s.end())
    45. {
    46. //cout << " ?" <<endl;
    47. j ++;
    48. // if(cnt == n) break;
    49. continue;
    50. }
    51. else
    52. {
    53. //cout << " !";
    54. if(cnt == a[j])
    55. {
    56. //cout << "?\n";
    57. s.insert(cnt);
    58. sum += cnt;
    59. cnt = 1;
    60. }
    61. else
    62. {
    63. cnt ++;
    64. // cout << "!\n";
    65. }
    66. j ++;
    67. }
    68. if(cnt > n) break;
    69. if(sum == res)
    70. {
    71. cout << sum << endl;
    72. return 0;
    73. }
    74. }
    75. maxn = max(maxn, sum);
    76. }
    77. cout << maxn;
    78. return 0;
    79. }

    3)Java

    1. import java.util.Scanner;
    2. // 1:无需package
    3. // 2: 类名必须Main, 不可修改
    4. public class Main {
    5. static int num;
    6. static int msize=0;
    7. public static void main(String[] args) {
    8. Scanner sc=new Scanner(System.in);
    9. num=sc.nextInt();
    10. int[] arr=new int[num];
    11. for (int i = 0; i < arr.length; i++) {
    12. arr[i]=sc.nextInt();
    13. }
    14. for (int i = 1; i <= num; i++) {
    15. msize+=i;
    16. }
    17. for (int i = 0; i < arr.length; i++) {
    18. barr=new boolean[num];
    19. P(arr,i,1,0);
    20. if(maxsize==msize)break;
    21. }
    22. System.out.println(maxsize);
    23. }
    24. static int maxsize=0;
    25. static boolean[] barr;
    26. public static void P(int[] arr,int k,int i,int size) {
    27. if(i>num||size==msize) {
    28. if(size>maxsize)maxsize=size;
    29. return;
    30. }
    31. if(barr[k]) {
    32. P(arr,k+1<arr.length?k+1:0,i,size);
    33. return;
    34. }
    35. if(arr[k]==i) {
    36. barr[k]=true;
    37. P(arr,k+1<arr.length?k+1:0,1,size+arr[k]);
    38. }else {
    39. P(arr,k+1<arr.length?k+1:0,i+1,size);
    40. }
    41. }
    42. }

    4)Python

    1. import os
    2. import sys
    3. # 请在此输入您的代码
    4. def f(n,nums):
    5. ans = 0
    6. for m in range(n): #控制次数 n个数有n种顺序读数,每一个数可以打头阵
    7. tmp = nums[m:] + nums[:m] #重新拼接数组
    8. j = 0 #数数,起点
    9. while j < max(tmp): #数的数比最大值还大,就跳出去,无法全部卡片收取
    10. for i,v in enumerate(tmp,1+j):
    11. if v == i:
    12. j = 0
    13. if len(tmp)==1: #只有一个,直接return出来
    14. return cnt
    15. elif v == tmp[-1]: #只剩一个,不好切片,所以用[:-1]
    16. tmp = tmp[:-1]
    17. else:
    18. r = tmp.index(v) #i已经不能用,已经再转圈计数了,所以用拼接的数组的索引
    19. tmp = tmp[r+1:] + tmp[:r]
    20. break
    21. else:
    22. j = i #转圈的i索引记录下来
    23. ans = max(ans, cnt - sum(tmp))
    24. return ans
    25. n = int(input())
    26. nums = [int(i) for i in input().split()]
    27. cnt = sum(nums)
    28. print(f(n,nums))

    adfdb5912024ac18326823996b935bb6.png

    02

    算法基础——前缀和、差分

    前缀和(Prefix Sum)的定义为:对于一个给定的数列 A,它的前缀和数列 S 是通过递推能求出来得770c80a8b4fdc93fbb88b62fa3ce309a.png部分和。

    例如:A = {1, 2, 3, 4, 5},S = {1, 3, 6, 10, 15},即:Sn = a1 + a2 + a3 + … + an,类似于数列的前 N 项和。

    差分,相当于前缀和的逆运算

    设 a[N] 是我们的目标数组,b[N] 是我们的差分数组,则它们的关系是:

    a1 = b1,a2 = b1 + b2,a3 = b1 + b2 + b3,…,an = b1 + b2 + … + bn,也就是说数组 a 是 数组 b 的前缀和。

    假如我们想要在数组 a 的 [l, r] 范围内每个元素都加上 c ,可以考虑迭代方式加上 c ,但是时间复杂度是 O(n)。

    利用数组 a 的差分数组 b 就可以这样做到:

    b[l] += c;

    b[r + 1] -= c;

    a 是 b 的前缀和,也就是说 b 中的每一个元素累加起来就是 a 中的元素,如果在 b 数组的某个位置加上 c ,a 是 b 的累加,那么从 a[l] 到 a[r] 中的每一个数都相当于加上了 c。

    ce5983459caa303f7bee0eeae3877fb3.gif

    练习题来啦!

    1、小明的彩灯

    204c346dc6dbfc31d34cafb734ea4a65.png

    参考代码

    1)C

    1. #include <stdio.h>
    2. #include <stdlib.h>
    3. #define N 500005
    4. long long arr[N];//原数组
    5. long long diff[N];//差分数组
    6. int main(int argc, char *argv[])
    7. {
    8. // 请在此输入您的代码
    9. int n, q,i=0;
    10. scanf("%d %d",&n,&q);
    11. while(++i<=n){
    12. scanf("%lld",&arr[i]);
    13. diff[i]=arr[i]-arr[i-1];///构造差分数组
    14. }
    15. while(q--){
    16. long long l,r,x;
    17. scanf("%lld %lld %lld",&l,&r,&x);
    18. diff[l]+=x;///对差分数组的两个端点进行修改
    19. if(r+1>=N)
    20. continue;
    21. diff[r+1]-=x;
    22. }
    23. ///区间修改已完成,开始还原数组
    24. for(i=1;i<=n;i++){
    25. diff [i]=diff[i]+diff[i-1];//还原修改后的数组
    26. if(diff [i]>=0)
    27. printf("%lld ",diff [i]);
    28. else
    29. printf("0 ");
    30. }
    31. return 0;
    32. }

    2)C++

    1. #include <iostream>
    2. const int maxn=500005;
    3. long long N[maxn]={0};
    4. long long b[maxn]={0};
    5. using namespace std;
    6. int main()
    7. {
    8. int P,Q;
    9. cin>>P>>Q;
    10. for(int i=1;i<=P;i++){
    11. cin>>N[i];
    12. b[i]=N[i]-N[i-1];//定义差分和数组
    13. }
    14. while(Q>0){
    15. long long l,r,x;
    16. cin>>l>>r>>x;
    17. b[l]+=x;
    18. b[r+1]-=x;//对两端进行操作
    19. Q--;
    20. }
    21. for(int i=1;i<=P;i++){
    22. N[i]=b[i]+N[i-1];//最后回归原来数组
    23. if(N[i]<0){
    24. cout<<0<<" ";
    25. }else
    26. cout<<N[i]<<" ";
    27. }
    28. return 0;
    29. }

    3)Java

    1. import java.io.BufferedReader;
    2. import java.io.IOException;
    3. import java.io.InputStreamReader;
    4. import java.io.StreamTokenizer;
    5. public class Main {
    6. public static void main(String[] args) throws IOException {
    7. // TODO Auto-generated method stub
    8. StreamTokenizer sc=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    9. sc.nextToken();
    10. int n=(int)sc.nval;
    11. sc.nextToken();
    12. int q=(int)sc.nval;
    13. long a[]=new long[n+1];
    14. long b[]=new long[n+1];
    15. for(int i=1;i<=n;i++){
    16. sc.nextToken();
    17. a[i]=(long)sc.nval;
    18. b[i]=a[i]-a[i-1];
    19. }
    20. for(int i=0;i<q;i++) {
    21. sc.nextToken();
    22. int l=(int)sc.nval;
    23. sc.nextToken();
    24. int r=(int)sc.nval;
    25. sc.nextToken();
    26. long c=(long)sc.nval;
    27. b[l]+=c;
    28. if(r<n)b[r+1]-=c;
    29. }
    30. for(int j=1;j<=n;j++)a[j]=a[j-1]+b[j];
    31. for(int i=1;i<=n;i++){
    32. if(a[i]<0)a[i]=0;
    33. System.out.print(a[i]+" ");
    34. }
    35. }
    36. }

    4)Python

    1. import os
    2. import sys
    3. N, Q = map(int, input().split())
    4. a = list(map(int, input().split()))
    5. op = [0 for _ in range(N+1)]
    6. for _ in range(Q):
    7. l, r, x = map(int, input().split())
    8. op[l-1] += x
    9. op[r] -= x
    10. for i in range(1, N):
    11. op[i]+= op[i-1]
    12. print(' '.join(str(max(a[i] + op[i], 0)) for i in range(N)))

    上周算法学与练就到这了,但「每日一题」仍在【算法刷题群】中继续,如想算法刷题,欢迎加入【算法刷题群】,期待你的到来!(ps:回复【算法】可获取更详细的算法题解~)

    476da248e7ae97b5ad18e4d3dd7e3000.png

  • 相关阅读:
    微信小程序开发实战(API及多人协调开发)
    lintcode 553 · 炸弹袭击【中等 数组+bfs+模拟】
    2022高教社杯数学建模比赛论文资料1.0
    VisionMaster自定义模块
    Windows10系统下安装GPU版Pytorch和MMDetection
    【面试普通人VS高手系列】死锁的发生原因和怎么避免
    Nginx禁止文件下载防止服务器被恶意扫描
    没有 RunInstallerAttribute.Yes 的公共安装程序
    读取vivo手机截图尺寸移动.jpg等文件
    vue3+ts打包报错处理
  • 原文地址:https://blog.csdn.net/MOY37RQW1JarN33BgZk/article/details/124937963