当前位置:   article > 正文

蓝桥杯C_C++/Java程序设计常用算法&技巧总结_蓝桥杯设计算法

蓝桥杯设计算法

精度处理

例如:我们想要程序判别 0.1+0.2 == 0.3

1.比较法

取等式差值的绝对值小于某一个特别小的数,若差值小于特别小的数则条件成立,反之。

  1. if( fabs(0.2 + 0.1 -0.3) <= 1E-10 ) //一般1的负10次方够用了
  2. cout<<"true"<<endl;
  3. else
  4. cout<<"false"<<endl;
  • 1
  • 2
  • 3
  • 4

2.整型比较

由于计算机对于整形的运算绝对精准,所以把浮点运算化整形运算的做法是我们常用的。
将等式两边分别乘以10

  1. if(1 + 2 == 3)
  2. cout<<"true"<<endl;
  3. else
  4. cout<<"false"<<endl;
  • 1
  • 2
  • 3
  • 4

最大公约数&最小公倍数

最大公约数

1.短除法

  1. /**短除法*/
  2. int enum_max_common_divisor(int a,int b)//大的值为b,枚举法
  3. {
  4. for(int i=a;i>=1;i--) //当两个数互质时,则最大公约数为I
  5. if(a%i==0 && b%i==0)
  6. return i;
  7. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.辗转除法

  1. /**辗转除法*/
  2. int max_common_divisor(int a,int b)//递归辗转除法,优点:a,b可以任意值输入
  3. {
  4. if(a==0) return b;
  5. return max_common_divisor(b%a,a);
  6. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

最小公倍数

最小公倍数 = a*b /最大公约数

  1. /*最小公倍数= a*b /最大公约数
  2. a= ka*i b= kb*i a*b = ka*kb*i*i */
  3. int min_common_multiple(int a,int b)
  4. {
  5. return a*b/max_common_divisor(a,b);
  6. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

递归

递归的特点:
1)递归就是方法里调用自身。
2)在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。
3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
4)在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等,所以一般不提倡用递归算法设计程序。

构造递归主要是构造出口(output)移动变量(moveParameter)

例子

1.斐波纳契数列(Fibonacci Sequence)

在数学上,斐波纳契数列以如下被以递归的方法定义:F1=1,F2=1,Fn=F(n-1)+F(n-2)(n>2,n∈N*))

  1. int fib(int index)
  2. {
  3. if(index==1||index==2)//出口
  4. {
  5. return 1;
  6. }
  7. else
  8. {
  9. return fib(index-1)+fib(index-2);
  10. }
  11. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.N的阶层

  1. int factorial(int index)
  2. {
  3. if(index==1)
  4. {
  5. return 1;
  6. }
  7. else
  8. {
  9. return factorial(index-1)*index;
  10. }
  11. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这里写图片描述

递归算法转换成为非递归算法

将递归算法转换为非递归算法有两种方法,一种是直接求值不需要回溯;另一种是不能直接求值需要回溯。前者使用一些变量保存中间结果,称为直接转换法;后者使用栈保存中间结果,称为间接转换法,下面分别讨论这两种方法。
  1. 直接转换法
      直接转换法通常用来消除尾递归和单向递归,将递归结构用循环结构来替代。尾递归是指在递归算法中,递归调用语句只有一个,而且是处在算法的最后。例如求阶乘的递归算法:
  1.   long fact(int n)
  2.   {
  3.   if (n==0)
  4.   return 1;
  5.   else
  6.   return n*fact(n-1);
  7.   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  当递归调用返回时,是返回到上一层递归调用的下一条语句,而这个返回位置正好是算法的结束处,所以
,不必利用栈来保存返回信息。对于尾递归形式的递归算法,可以利用循环结构来替代。例如求阶乘的递归算法
可以写成如下循环结构的非递归算法:

  1. long fact(int n)
  2.   {
  3.   int s=1;
  4.   for (int i=1; i<n;i++)
  5.   s=s*i; //用s保存中间结果
  6.   return s;
  7.   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  单向递归是指递归算法中虽然有多处递归调用语句,但各递归调用语句的参数之间没有关系,并且这些递归
调用语句都处在递归算法的最后。显然,尾递归是单向递归的特例。例如求斐波那契数列的递归算法如下:

  1. int f(int n)
  2.   {
  3.   if (n= =1 | | n= =0)
  4.   return 1;
  5.   else
  6.   return f(n-1)+f(n-2);
  7.   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  对于单向递归,可以设置一些变量保存中间结构,将递归结构用循环结构来替代。例如求斐波那契数列的算
法中用s1和s2保存中间的计算结果,非递归函数如下:

  1. int f(int n)
  2. {
  3.   int a[n+2];
  4. a[1]=1,a[2]=1;
  5.   for(i=3;i<=n;i++)
  6. {
  7. a[i] = a[i-1] + a[i-2];
  8. }
  9.   return a[n];
  10. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

递归经典竞赛应用

李白打酒

话说大诗人李白,一生好饮。幸好他从不开车。

一天,他提着酒壶,从家里出来,酒壶中有酒2斗。他边走边唱:

逢店加一倍,遇花喝一斗。

这一路上,他一共遇到店5次,遇到花10次,已知最后一次遇到的是花,他正好把酒喝光了。

请你计算李白遇到店和花的次序,可以把遇店记为a,遇花记为b。

  1. int total=0;
  2. int a(int s,int f,int d)
  3. {
  4. if(s>0) //当店大于0,就行搜索
  5. a(s-1,f,d*2); //return a(s-1,f,d*2);
  6. if(f>1) //花大于1,进行搜索
  7. a(s,f-1,d-1);
  8. if(s==0 && f==1 && d==1) //保证最后一次遇见的是花 此时还剩下1斗酒
  9. total++;
  10. return total;
  11. }
  12. int main()
  13. {
  14. printf("%d",a(5,10,2)); //初始化为最初有 5个店 10个花 2斗酒
  15. return 0;
  16. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

第39级台阶

  1. #include <iostream>
  2. using namespace std;
  3. #include <cstdio>
  4. #include <cstdlib>
  5. #include <cmath>
  6. #include <algorithm>
  7. #define LEFT false
  8. #define RIGHT true
  9. static int total;
  10. void f(int step,bool flag)
  11. {
  12. if(step>39)//出口
  13. return ;
  14. if(flag==RIGHT && step==39)//判断条件是否符合
  15. {
  16. total ++;
  17. return;
  18. }
  19. f(step+1,!flag);//遍历迈一步的方式
  20. f(step+2,!flag);//遍历迈两步的方式
  21. }
  22. int main()
  23. {
  24. f(1,LEFT); //第一步迈一步
  25. f(2,LEFT);//第一步迈两步
  26. cout<<total;
  27. return 0;
  28. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

返回累加性解法

  1. #define LEFT false
  2. #define RIGHT true
  3. static int total;
  4. long f(int step,bool flag)
  5. {
  6. if(step == 1)//判断第一步左脚是一步
  7. {
  8. if(flag == RIGHT)
  9. return 1;
  10. else
  11. return 0;
  12. }
  13. else if(step == 2)//判断第一步左脚是两步,必定最后一步为右脚
  14. {
  15. return 1;
  16. }
  17. if(flag==RIGHT && step==0)
  18. {
  19. return 1;
  20. }
  21. else if(step<=0)
  22. {
  23. return 0;
  24. }
  25. return f2(step-1,!flag)+f2(step-2,!flag);
  26. }
  27. int main()
  28. {
  29. cout<<f(39,LEFT)<<endl;
  30. cout<<total;
  31. return 0;
  32. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

DFS

回溯型DFS

字符串全排列

DFS构建步骤
1.设定step出口变量,在出口处拦截适宜的数据;
2.构造原数据数组old && 移动数据数组a[];
3.for循环处对数据进行排列置换
4.for循环步骤 :判断使用标志位i下标的数据为未被使用,条件成立->置该数组下标使用标志->置换数据a[step] = old[i],注意前者出口移动变量后者i变量->递归dfs(step+1),使函数步移->回溯,置该标志位数组下标未使用标志
  1. #define ARRAY_LENGTH 4
  2. #define NOT_USED 0
  3. #define USED 1
  4. char old[ARRAY_LENGTH] = {'a','b','c','d'};
  5. char a[ARRAY_LENGTH];
  6. int useFlag[ARRAY_LENGTH];
  7. void dfs(int step)
  8. {
  9. if(step >= ARRAY_LENGTH) //出口,过滤数据找出结果
  10. {
  11. for(int i=0;i<ARRAY_LENGTH;i++)
  12. {
  13. cout<<a[i]<<" ";
  14. }
  15. cout<<endl;
  16. return ;
  17. }
  18. for(int i=0;i<ARRAY_LENGTH;i++)
  19. {
  20. if( useFlag[i] == NOT_USED) //判断该下标的标志位是否被使用
  21. {
  22. useFlag[i] = USED; //置使用标志
  23. a[step] = old[i]; //置换数据,排列数组
  24. dfs(step+1); //递归
  25. useFlag[i] = NOT_USED; //回溯
  26. }
  27. }
  28. }
  29. int main(void)
  30. {
  31. memset(useFlag,NOT_USED,sizeof(useFlag));
  32. dfs(0);
  33. return 0;
  34. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

从for循环角度理解不回溯型DFS

小明与其他3人玩牌。
一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张。
这时,小明脑子里突然冒出一个问题:
如果不考虑花色,只考虑点数,也不考虑自己得到的牌的先后顺序,自己手里能拿到的初始牌型组合一共有多少种呢?

  1. int main()
  2. {
  3. int sum =0;
  4. int total=0;
  5. int a[14];
  6. for( a[1]=0;a[1]<=4;a[1]++) //1
  7. for( a[2]=0;a[2]<=4;a[2]++) //2
  8. for( a[3]=0;a[3]<=4;a[3]++) //3
  9. for( a[4]=0;a[4]<=4;a[4]++) //4
  10. for( a[5]=0;a[5]<=4;a[5]++) //5
  11. for( a[6]=0;a[6]<=4;a[6]++) //6
  12. for( a[7]=0;a[7]<=4;a[7]++) //7
  13. for( a[8]=0;a[8]<=4;a[8]++) //8
  14. for( a[9]=0;a[9]<=4;a[9]++) //9
  15. for( a[10]=0;a[10]<=4;a[10]++) //10
  16. for( a[11]=0;a[11]<=4;a[11]++) //11
  17. for( a[12]=0;a[12]<=4;a[12]++) //12
  18. for( a[13]=0;a[13]<=4;a[13]++) //13
  19. {
  20. for(int i=1;i<=13;i++)
  21. {
  22. sum += a[i];
  23. }
  24. if(sum == 13)
  25. {
  26. total++;
  27. }
  28. sum=0;
  29. }
  30. cout<<total;
  31. return 0;
  32. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  1. /**
  2. parameter: n:步移 ; cartNum:手上的牌数量 ; 这两个变量为控制出口变量。
  3. */
  4. void dfs(int n,int cartNum)
  5. {
  6. if(n>14) //出口1:抽牌的次数(13次)越界 相当于for循环阶数:13阶
  7. {
  8. return;
  9. }
  10. if(cartNum>=13) //出口2:牌数量越界
  11. {
  12. if(cartNum==13) //截取条件成立的结果
  13. sum++;
  14. return;
  15. }
  16. else //相当于for循环中的条件int a=0~a<=4;
  17. {
  18. dfs(n+1,cartNum); //没有该类的牌
  19. dfs(n+1,cartNum+1); //有一张该类的牌
  20. dfs(n+1,cartNum+2); //有两张该类的牌
  21. dfs(n+1,cartNum+3); //有三张该类的牌
  22. dfs(n+1,cartNum+4); //有四张该类的牌
  23. }
  24. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

竞赛应用

带分数

  1. 100 可以表示为带分数的形式:100 = 3 + 69258 / 714
  2. 还可以表示为:100 = 82 + 3546 / 197
  3. 注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
  4. 类似这样的带分数,10011 种表示法。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

题目要求:
从标准输入读入一个正整数N (N<1000*1000)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。
注意:不要求输出每个表示,只统计有多少表示法!

例如:
用户输入:
100
程序输出:
11

再例如:
用户输入:
105
程序输出:
6

资源约定:
峰值内存消耗 < 64M
CPU消耗 < 3000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

解法一

dfs N=A+B/C⇨B=(N-A)*C。根据公式,只需要求A和C就可以进行判断了。A的范围是1~N,但是这些数中有很多是无用的(如:11,101……)。我们只选有用的数,用vis数组标记1~9中已经用过的数字,避免做不必要的搜索。
  1. #include <iostream>
  2. using namespace std;
  3. #include<algorithm>
  4. #include <cstdio>
  5. #include <cstdlib>
  6. #include <cstring>
  7. int ans = 0; //
  8. bool vis[10];
  9. int n; int A = 0, C = 0;
  10. int lenN; int ALEN = 0, CLEN = 0;
  11. bool judge() {
  12. bool vistemp[10];
  13. memcpy(vistemp, vis, sizeof(vis));
  14. int t = (n - A)*C; int BLEN = 0;
  15. while (t) {
  16. if (vistemp[t % 10]) return false;
  17. vistemp[t % 10] = 1;
  18. BLEN++;
  19. t /= 10;
  20. }
  21. return BLEN+ALEN+CLEN==9;
  22. }
  23. void dfs(int flag)
  24. {
  25. if (flag == 2)
  26. {
  27. if (judge()) ans++;
  28. return;
  29. }
  30. for (int i = 1; i <= 9; i++)
  31. {
  32. if (vis[i]) continue;
  33. vis[i] = 1;
  34. if (flag == 1)
  35. {
  36. A *= 10; A += i; ALEN += 1;
  37. if (A<=n)
  38. {
  39. dfs(1);
  40. dfs(3);
  41. ALEN -= 1; A /= 10;
  42. }
  43. else if (A > n)
  44. {
  45. ALEN -= 1; A /= 10;
  46. vis[i] = 0;
  47. break;
  48. }
  49. }
  50. else if (flag == 3) {
  51. if (CLEN < (9 - ALEN) / 2)
  52. {
  53. C *= 10; C += i; CLEN++;
  54. dfs(2);
  55. dfs(3);
  56. C /= 10; CLEN--;
  57. }
  58. else { vis[i] = 0; break; }
  59. }
  60. vis[i] = 0;
  61. }
  62. }
  63. int main() {
  64. cin >> n;
  65. int temp = n;
  66. lenN = 0;
  67. while (temp) { lenN++; temp /= 10; }
  68. memset(vis, 0, sizeof(vis));
  69. vis[0] = 1;
  70. dfs(1);
  71. cout << ans << endl;
  72. return 0;
  73. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

地宫取宝

  1. X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。
  2. 地宫的入口在左上角,出口在右下角。
  3. 小明被带到地宫的入口,国王要求他只能向右或向下行走。
  4. 走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可以拿起它(当然,也可以不拿)。
  5. 当小明走到出口时,如果他手中的宝贝恰好是k件,则这些宝贝就可以送给小明。
  6. 请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这k件宝贝。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

【数据格式】

  1. 输入一行3个整数,用空格分开:n m k (1<=n,m<=50, 1<=k<=12)
  2. 接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值
  3. 要求输出一个整数,表示正好取k个宝贝的行动方案数。
  4. 该数字可能很大,输出它对 1000000007 取模的结果。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

例如,输入:
2 2 2
1 2
2 1
程序应该输出:
2

再例如,输入:
2 3 2
1 2 3
2 1 5
程序应该输出:
14

资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。

注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

解法一

4维的记忆化DFS,题目要求只能下或右走,从左上角走到右下角,记录下每个坐标下的数量和最大宝贝价值下的走的情况
  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<string>
  4. #include<algorithm>
  5. #include<math.h>
  6. #include<iostream>
  7. #include<time.h>
  8. #define mod 1000000007
  9. using namespace std;
  10. int n,m,k;
  11. int c[55][55];
  12. int dp[55][55][101][15];
  13. int dfs(int x,int y,int mun,int v) //x,y表示坐标,mun表示手中宝贝的数量,V表示手中的最大宝贝的价值
  14. {
  15. if(dp[x][y][mun][v]!=-1)//记录了经过 此坐标此数量此价值下的所有走法,
  16. return dp[x][y][mun][v];//所以直接返回,后面的无需走了
  17. if(x==n-1&&y==m-1)
  18. {
  19. if(mun==k||(mun==k-1&&c[x][y]>v))//符合要求的算一种
  20. return dp[x][y][mun][v]=1;
  21. else
  22. {
  23. return dp[x][y][mun][v]=0;//不符合
  24. }
  25. }
  26. int t=0;
  27. if(y+1<m))//x<n-1
  28. {
  29. if(c[x][y]>v)//选择拿起宝贝
  30. {
  31. t=(t+dfs(x,y+1,mun+1,c[x][y]))%mod;
  32. }
  33. t=(t+dfs(x,y+1,mun,v))%mod;//选择不拿
  34. }
  35. if(x+1<n)//x<n-1
  36. {
  37. if(c[x][y]>v)
  38. {
  39. t=(t+dfs(x+1,y,mun+1,c[x][y]))%mod;
  40. }
  41. t=(t+dfs(x+1,y,mun,v))%mod;
  42. }
  43. return dp[x][y][mun][v]=t%mod;//由右和下返回的值记录于此坐标下,下次路过此时
  44. //不再递归
  45. }
  46. int main()
  47. {
  48. memset(dp,-1,sizeof(dp)); //标志
  49. scanf("%d%d%d",&n,&m,&k);
  50. for(int i=0;i<n;i++)
  51. for(int j=0;j<m;j++)
  52. {
  53. scanf("%d",&c[i][j]);
  54. c[i][j]++;//为什么加1,因为有的宝贝价值问0,会出现比较错误
  55. }
  56. printf("%d\n",dfs(0,0,0,0));
  57. return 0;
  58. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

贪心

动态规划

常用函数

C常用头文件

标准输入输出

#include < cstdio >

scanf()

printf()

标准库

#include < cstdlib >

qsort()

数学库

#include < cmath>

字符串库

#include < cstring >

memset(array_first_address, NUM, sizeof(array));

C常用头文件

标准输入输出

#include < iostream >
using namespace std;

cin>>

cout<<

算法库

#include < algorithm >

next_permutation(array_first_address , array_first_address+length);

sort(array_first_address , array_first_address+length);

其他技巧

康托展开式公式:

X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!

这个式子是由1到n这n个数组成的全排列,共n!个,按每个全排列组成的数从小到大进行排列,并对每个序列进行编号(从0开始),并记为X。
比如说1到4组成的全排列中,长度为4,1234对应编号0,1243对应编号1
那a1,a2,a3,a4是什么意思呢?==>>
对1到4的全排列中,我们来考察3214,则
a4={3在集合(3,2,1,4)中是第几大的元素}=2
a3={2在集合(2,1,4)中是第几大的元素}=1
a2={1在集合(1,4)中是第几大的元素}=0
a1=0(最后只剩下一项)
则X=2*3!+1*2!+0*1!+0*0!=14,即3214对应的编号为14。
因此,此题用康托展开式就可以出来了,将abcd…写成1,2,3,4…就行了。
  1. long long a[ 17 ];
  2. long long fun(long long n) //求阶乘
  3. {
  4. long long s = 1;
  5. for(int i = 1;i<=n;i++)
  6. s*=i;
  7. return s;
  8. }
  9. int main()
  10. {
  11. long long t;
  12. int f[] = {2,3,11,6,17,12,1,10,8,5,13,7,9,15,4,14,16};
  13. long long sum = 0;
  14. a[0] = 1;
  15. for(int i=1;i<17;i++)
  16. {
  17. a[i] = a[i-1]*i;
  18. }
  19. for(int i = 0;i<16;i++)
  20. {
  21. t = 0;
  22. for(int j = i+1;j<17;j++)
  23. {
  24. if(f[j]<f[i])
  25. {
  26. t++;
  27. }
  28. }
  29. sum += (t)*a[17-1-i]; //康托展开式公式
  30. }
  31. printf("%lld",sum);
  32. return 0;
  33. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/222699
推荐阅读
相关标签
  

闽ICP备14008679号