2021授课
面向大众
1.递归
递归
三要素:
- 这个函数是干什么的
- 递归结束条件是什么(就是找特殊的)
- 找出函数的等价关系
例子:
1.求n的阶乘
函数是干什么的?定义函数int f( int n ) 表示求n的阶乘,
递归结束条件? n等于1时,返回1嘞,n等于2时,返回2嘞 ( n <= 2 就返回 n本身 )
函数等价关系? f(n) = n * f(n-1)
10!=1*2*3*4*5*6*7*8*9*10
9! =1*2*3*4*5*6*7*8*9
发现:10! = 10 * 9!
函数关系 f(10) = 10 * f(9)
对应n的关系: f(n) = n * f(n-1)
2.斐波那锲数列, 求这个数列的第n项
函数是干什么的? 定义函数int f( int n ) 表示求第n项
递归结束条件? n等于1或者n等于2时 返回 都是 1
函数等价关系? f(n) = f(n-1) + f(n-1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
1 1 2 3 5 8 13 21 34 55 89 144 233 377.............
从第三项开始,第n项是第n-1项 + 第n-2项
f(n) = f(n-1) + f(n-2)
#include <iostream>
using namespace std;
int f( int n )
{
if ( n <= 2 ) {
return 1;
}
return f(n-1) + f(n-2);
}
int main()
{
int n;
for ( int i = 1; i <= 41; ++i ) {
cout << "第" << i << "项" << f(i) << '\n';
}
return 0;
}
递归优化
1.考虑是否重复计算
2.是否可以自底向上解决问题 (递推)
题目实战
⼀只青蛙⼀次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上⼀个n级的台阶总共有
多少种跳法。
函数是干什么的? 定义函数int f( int n ) 表示青蛙跳到第n级台阶的跳法种数
递归结束条件? n<=0时,结果为0 ; n == 1 时,结果为1 ; n == 2 时,结果为2;
函数等价关系? 青蛙跳到第n级台阶,可以从第n-1级直接跳1级上去,也可以从n-2级台阶直接跳2级上去
得出函数关系式: f(n) = f(n-1) + f(n-2);
#include <iostream>
using namespace std;
int f( int n )
{
if ( n <= 0 ) {
return 0;
}
else if ( n == 1 ) {
return 1;
}
else if ( n == 2 ) {
return 2;
}
return f(n-1) + f(n-2);
}
int main()
{
int n;
cin >> n;
cout << "青蛙跳到第" << n << "级台阶的跳法:" << f(n) << endl;
return 0;
}
#include <iostream>
using namespace std;
int a[1005];
int f( int n )
{
if ( n <= 0 ) {
return 0;
}
else if ( a[n] ) {
return a[n];
}
else if ( n == 1 ) {
return a[n] = 1;
}
else if ( n == 2 ) {
return a[n] = 2;
}
return a[n] = f(n-1) + f(n-2);
}
int main()
{
int n;
cin >> n;
f(n);
cout << "青蛙跳到第" << n << "级台阶的跳法:" << a[n] << endl;
return 0;
}
⼀个机器⼈位于⼀个 m x n ⽹格的左上⻆ (起始点在下图中标记为“Start” )。
机器⼈每次只能向下或者向右移动⼀步。机器⼈试图达到⽹格的右下⻆(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
函数是干什么的? 定义函数int f( int x, int y ) 表示机器人走到此坐标的路径数
递归结束条件? x==1&&y!=1 结果为1;同理,x!=1&& y ==1时,结果同样为1
函数等价关系? 机器人走到(x,y) 可以从 (x-1,y) 或者 (x, y-1)这两个坐标走过来,
得出函数关系式: f(x, y) = f(x-1, y) + f(x, y-1);
#include <iostream>
using namespace std;
int f( int x, int y );
int main()
{
int n, m;
cin >> n >> m;
cout << "走法的种数:" << f(n, m);
return 0;
}
int f( int x, int y )
{
if ( (x == 1 && y != 1) || ( y == 1 && x != 1 ) ) {
return 1;
}
return f(x-1, y) + f(x, y-1);
}
#include <iostream>
using namespace std;
const int N = 1e4+5;
int dp[N][N];
int f( int x, int y );
int main()
{
int n, m;
cin >> n >> m;
cout << "走法的种数:" << f(n, m);
return 0;
}
int f( int x, int y )
{
if ( dp[x][y] ) {
return dp[x][y];
}
if ( (x == 1 && y != 1) || ( y == 1 && x != 1 ) ) {
return dp[x][y] = 1;
}
return dp[x][y] = f(x-1, y) + f(x, y-1);
}
题目:2^n * 2^n的矩阵,初始全为1,现在修改,改为每个左上角小矩阵全为0,
#include <iostream>
using namespace std;
const int N = 1025;
int s[N][N], n, b = 1;
void f( int l, int x, int y )
{
if ( l == 2 ) {
s[x][y] = 0;
return ;
}
for ( int i = x; i <= x+l/2-1; ++i ) {
for ( int j = y; j <= y+l/2-1; ++j ) {
s[i][j] = 0;
}
}
f(l/2, x+l/2, y);
f(l/2, x+l/2, y+l/2);
f(l/2, x, y+l/2);
}
int main()
{
cin >> n;
for ( int i = 1; i <= n; ++i ) {
b *= 2;
}
for ( int i = 1; i <= b; ++i ) {
for ( int j = 1; j <= b; ++j ) {
s[i][j] = 1;
}
}
f(b, 1, 1);
for ( int i = 1; i <= b; ++i ) {
for ( int j = 1; j <= b; ++j ) {
cout << s[i][j] << ' ';
}
cout << '\n';
}
return 0;
}
2.搜索/遍历
深度优先
深度优先遍历
深度优先遍历主要思路是从图中⼀个未访问的顶点 V 开始,沿着⼀条路⼀直⾛到底,然后从这条路尽头
的节点回退到上⼀个节点,再从另⼀条路开始⾛到底...,不断递归重复此过程,直到所有的顶点都遍历
完成,它的特点是不撞南墙不回头,先⾛完⼀条路,再换⼀条路继续⾛
例子:n的全排列
3
123 132 213 231 312 321
#include <iostream>
using namespace std;
int n;
int num[20], sign[20];
//此函数表示往第now个盒子里面放数字
void dfs( int now )
{
if ( now == n+1 ) {
for ( int i = 1; i <= n; ++i ) {
cout << num[i];
}
cout << '\n';
return ;
}
for ( int i = 1; i <= n; ++i ) {
if ( !sign[i] ) {
sign[i] = 1;
num[now] = i;
//表示往第now+1个盒子里面放数字,即进入下一步
dfs(now+1);
sign[i] = 0;
}
}
}
int main()
{
cin >> n;
dfs(1);
return 0;
}
迷宫问题
给定n*m的矩阵迷宫,起点x,y, 终点x1,y1,求出起点到终点的最短距离
每次只能有上下左右四个方向可以走,矩阵为1表示这是障碍物不能通过。
5 4 五行四列
0 0 1 0 /*
0 0 0 0
0 0 1 0
0 1 0 0
0 0 0 1 这是迷宫*/
1 1 4 3 // 这是起点终点
输出:7
#include <iostream>
using namespace std;
//矩阵数组,标记数组
int n, m, mi[10][10], sign[10][10];
int x, y, x1, y1, mins = 0x3f3f3f3f;
//方向
int direct[4][2] = {
{-1,0},{0,1},{1,0},{0,-1}
};
void dfs( int l, int r, int step )
{
if ( l == x1 && r == y1 ) {
mins = min(mins, step);
}
for ( int i = 0; i < 4; ++i ) {
int tx = l + direct[i][0];
int ty = r + direct[i][1];
if ( mi[tx][ty] == 1 || tx < 1 ||
ty < 1 || tx > n || ty > m
|| sign[tx][ty] ) {
continue;
}
else {
sign[tx][ty] = 1;
dfs(tx, ty, step+1);
sign[tx][ty] = 0;
}
}
}
int main()
{
cin >> n >> m;
for ( int i = 1; i <= n ; ++i ) {
for ( int j = 1; j <= m; ++j ) {
cin >> mi[i][j];
}
}
cin >> x >> y >> x1 >> y1;
sign[x][y] = 1;
dfs(x, y, 0);
cout << mins << endl;
return 0;
}
广度优先
⼴度优先遍历,指的是从图的⼀个未遍历的节点出发,先遍历这个节点的相邻节点,再依遍次历每个相邻节点的相邻节点。
树的层序遍历
接上一讲迷宫问题:
现在用广度优先来解决问题:
#include <iostream>
#include <queue>
using namespace std;
//矩阵数组,标记数组
int n, m, mi[10][10], sign[10][10];
int x, y, x1, y1, mins = 0x3f3f3f3f;
//方向
int direct[4][2] = {
{-1,0},{0,1},{1,0},{0,-1}
};
typedef struct{
int l, r, step;
}Node;
void bfs()
{
queue<Node> q;
Node node = {x, y, 0};
q.push(node);
while ( !q.empty() ) {
Node temp = q.front();
for ( int i = 0; i < 4; ++i ) {
int tx = temp.l + direct[i][0];
int ty = temp.r + direct[i][1];
if ( mi[tx][ty] == 1 || tx < 1 ||
ty < 1 || tx > n || ty > m
|| sign[tx][ty] ) {
continue;
}
else {
sign[tx][ty] = 1;
Node t = { tx, ty, temp.step+1};
if ( tx == x1 && ty == y1 ) {
mins = min(mins, t.step);
}
q.push(t);
}
}
q.pop();
}
}
int main()
{
cin >> n >> m;
for ( int i = 1; i <= n ; ++i ) {
for ( int j = 1; j <= m; ++j ) {
cin >> mi[i][j];
}
}
cin >> x >> y >> x1 >> y1;
sign[x][y] = 1;
bfs();
cout << mins << endl;
return 0;
}
图的邻接矩阵存储与遍历
用一个n*n的矩阵表示两个顶点之间的关系
例如:2 -> 3 m[2] [3] = 1, 如果是无向图,只需要反过来存一遍即可,如果边还带有权值,只需要把权值存起来即可
##无向图
输入一个n,表示图的定点数,从1开始计数,
输入一个m,表示边,
输出:遍历序列
5 4
1 2
3 4
2 3
1 5
#include <iostream>
using namespace std;
const int N = 1e3+5;
int m[N][N], n, k;//n个顶点,k条边
int sign[N];
void dfs( int now )
{
cout << now << ' ';
sign[now] = 1;
for ( int i = 1; i <= n; ++i ) {
if ( m[now][i] && !sign[i] ) {
sign[i] = 1;
dfs(i);
}
}
}
int main()
{
int s, e;
cin >> n >> k;
for ( int i = 1; i <= k; ++i ) {
cin >> s >> e;
m[s][e] = 1;
m[e][s] = 1;
}
dfs(1);
return 0;
}
#include <iostream>
#include <queue>
using namespace std;
const int N = 1e3+5;
int m[N][N], n, k;//n个顶点,k条边
int sign[N];
void bfs()
{
queue<int> q;
q.push(1);
sign[1] = 1;
while( !q.empty() ) {
int temp = q.front();
cout << temp << ' ';
for ( int i = 1; i <= n; ++i ) {
if ( m[temp][i] && !sign[i] ) {
q.push(i);
sign[i] = 1;
}
}
q.pop();
}
}
int main()
{
int s, e;
cin >> n >> k;
for ( int i = 1; i <= k; ++i ) {
cin >> s >> e;
m[s][e] = 1;
m[e][s] = 1;
}
bfs();
return 0;
}
思考题:
提示:这章讲的是搜索,肯定是希望你们用搜索解出来啦。。。long long
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
3.位运算
整数、二进制
>> | 右移 |
---|---|
<< | 左移 |
& | 按位与 |
| | 按位或 |
~ | 取反 |
^ | 按位异或(支持交换律) abc^d = acd^b |
右移>>,左移<<
>> <<:( 这里假设只有三位 ) 二进制位都向右边移动n位(n是运算符右边的数字),左边不够补0; 例如:4->二进制100 ==> 4>>1(表示4对应的二进制位向右移动一位) ==> 010 左移:理解同右移 例如:4->二进制100 ==> 4<<1(表示4对应的二进制位向左移动一位) ==> 000
基本的四种位运算
以下都假设只有四位二进制
&:二进制位对应,都为1时,结果才为1(可以理解为且,且只有都为1时结果才为1) 例如:9->二进制1001 10->二进制1010 1->二进制0001 0->二进制0000 9&1: 1001 & 0001 等于:0001 9&10: 1001 & 1010 等于:1000 |:按位或 对应二进制,都为0时结果才为0;^:按位异或 对应二进制,相同为0,不同为1;说明一个整数与自身异或得0,一个整数与0异或得自身~:取反, 自己的二进制,1变为0,0变为1;
题目演练:
1.判断奇偶
/* 给你一个整数,判断奇偶 用n&1 ==1就是奇数,反之就是偶数*/#include <iostream>using namespace std;int main(){ int n; cin >> n; if ( n & 1 ) { cout << "这是奇数" << endl; } else { cout << "这是偶数" << endl; } return 0;}
2.翻转数字
/*给定一个十进制整数,输出它在二进制下的翻转结果。Input: 43261596 (00000010100101000001111010011100)Output: 964176192 (00111001011110000010100101000000)*/#include <iostream>using namespace std;int main(){ int n; int ans = 0; cin >> n; for ( int i = 0; i < 32; ++i ) { ans <<= 1; ans += ( n & 1 ); n >>= 1; } cout << ans << endl; return 0;}
3.找不重复的
/* 给定一个整数数组,这个数组里只有一个数次出现了一次,其余数字出现了两次,求这个只出现一次的数字。Input: 5 4 1 2 1 2Output: 4用异或运算*/#include <iostream>using namespace std;int main(){ int n, a[105], ans = 0; cin >> n; for ( int i = 0; i < n; ++i ) { cin >>a[i]; } for ( int i = 0; i < n; ++i ) { ans ^= a[i]; } cout << ans << endl; return 0;}
4.跟第三题类似
/* 给一个整数n,输入n+1个数字,这些数字在1~n的范围内,并且这n+1个数字中只有一对重复的数字,请找出重复的数字 5 1 2 3 3 4 5 3*/#include <iostream>using namespace std;int main(){ int n, num, ans = 0; cin >> n; for ( int i = 0; i <= n; ++i ) { cin >> num; ans ^= num; } for ( int i = 1; i <= n; ++i ) { ans ^= i; } cout << ans <<'\n'; return 0;}
/*输入:1314520 输出:249036820*/#include <iostream>#include <cstdio>using namespace std;int main(){ unsigned int n; scanf("%u", &n); printf("%u\n", (n >> 16) + (n << 16)); return 0;}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
4.前缀和与差分
前缀和:
题目:(一维)
前缀和是一种重要的预处理,能大大降低查询的时间复杂度
给定一个有n个整数的数组,对这这个数组进行m次查询,输出每次查询l到r区间([l,r])的所有数字的和例如:5 21 2 3 4 51 32 4输出:69
/*原数组num[n]重新定义一个数组a[n],a[i]表示num数组从下标1到n所有数字的和a[0] = 0 ( num[0] );a[1] = num[1];a[2] = num[1] + num[2];a[3] = num[1] + num[2] + num[3];a[4] = num[1] + num[2] + num[3] + num[4];......................*/结论查询[l,r] 只需要a[r]-a[l-1]即可!!!!!!!!!!!!!!!!
#include <iostream>
using namespace std;
int n, m, a[105], num[105];
int main()
{
cin >> n >> m;
for ( int i = 1; i <= n; ++i ) {
cin >> num[i];
a[i] = a[i-1] + num[i];
}
while (m--) {
int l ,r;
cin >> l >> r;
cout << a[r] - a[l-1] << '\n';
}
return 0;
}
题目(二维)
二维查询
输入一个n*n的矩阵,数组数字在-100~100之间,n<=100,
进行m次查询,每次查询(i,j)为左上角,(x,y)为右下角的子阵的所有元素的和;
例如:
4 1 (4*4矩阵,查询1次)
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2(输入的矩阵)
1 1 2 2 (查询1,1为左上角,2,2为右下角矩阵的里面所有元素和)
输出:9
//代码/*4 10 -2 -7 0 9 2 -6 2-4 1 -4 1 -1 8 0 -21 1 2 2 */#include <iostream>using namespace std;int n, m;int num[105][105], a[105][105];int main(){ cin >> n >> m; for ( int i = 1; i <= n; ++i ) { for ( int j = 1; j <= n; ++j ) { cin >> num[i][j]; a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + num[i][j]; } } while ( m-- ) { int i, j, x, y; cin >> i >> j >> x >> y; int temp = a[x][y] - a[i-1][y] - a[x][j-1] + a[i-1][j-1]; cout << temp << '\n'; } return 0;}
题目:最大子阵和
输入n*n的矩阵,输出最大子阵的和;
/*
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
15
*/
#include <iostream>
using namespace std;
int n, a[122][122];
int main()
{
int num, maxn = -0x3f3f3f3f, temp;
cin >> n;
for ( int i = 1; i <= n; ++i ) {
for ( int j = 1; j <= n; ++j ) {
cin >> num;
a[i][j] = a[i-1][j] + a[i][j-1] + num - a[i-1][j-1];
}
}
for ( int i = 1; i <= n; ++i ) {
for ( int j = 1; j <= n; ++j ) {
for ( int x = i; x <= n; ++x ) {
for ( int y = j; y <= n; ++y ) {
temp = a[x][y] - a[x][j-1] - a[i-1][y] + a[i-1][j-1];
maxn = max(maxn, temp);
}
}
}
}
cout << maxn << endl;
return 0;
}
差分:
一般用于区间里面的元素同时修改。
例子:给定一个有n个数的整数数组,对他进行m次修改,每次修改都是使[l,r]区间的所有元素都加一,
求m次操作后的数组序列。
例如原数组a为:1 3 1 4 5 2 0
对它进行3次修改:[1,5] [2,4] [3,3];
新数组a1:2 5 4 6 6 2 0
从下标1开始,现在我们构建这样一个数组:c[n],原数组用a[n]表示;c[i]表示a[i]-a[i-1],c[1]=a[1],总结来说,c数组存的就是原数组相邻元素的差
c数组: 1 2 -2 3 1 -3 -2
再来看一下,对c数组求一下他的前缀和!!!!
为什么差分数组的前缀和是原数组?
c[i] = a[i] - a[i-1];
c[1] = a[1];
c[2] = a[2] - a[1];
c[3] = a[3] - a[2];
c[4] = a[4] - a[3];
c[5] = a[5] - a[4];
c[6] = a[6] - a[5];
c[1]+c[2] = a[1] + a[2] - a[1] = a[2];
c[1]+c[2]+c[3] = a[1] + a[2] - a[1] + a[3] - a[2] = a[3];
...........................
区间修改怎么来维护呢?
例如原数组a为:1 3 1 4 5 2 0 差分c数组 :1 2 -2 3 1 -3 -2 对原数组进行3次修改: [1,5]第一次修改:2 4 2 5 6 2 0 上次差分数组: 1 2 -2 3 1 -3 -2 差分c数组: 2 2 -2 3 1 -4 -2 [2,4]第二次修改:2 5 3 6 6 2 0 上次差分数组: 2 2 -2 3 1 -4 -2差分c数组: 2 3 -2 3 0 -4 -2 [3,3]第三次修改:2 5 4 6 6 2 0 上次差分数组: 2 3 -2 3 0 -4 -2 差分c数组: 2 3 -1 2 0 -4 -2结论:对[l,r]区间修改时,差分数组c[l] +(-) 1, c[r+1] -(+) 1;
代码:
差分题目实战:
小IT接到一个任务,摆在小IT面前的是 n 个空瓶,小it会收到 k 条命令,命令都是"A B"的形式,意思是要小IT往范围为A...B的空瓶里放一枚金币,例如:如果命令是7 9,那么小IT要往第7,8,9个空瓶里放一枚金币。在小IT完成所有命令后,需要给上司发送一条信息,表示这 n 个瓶子所装金币的中位数是多少。输入格式:第一行包含一个整数 n(1≤n≤106),表示瓶子的数量。第二行包含一个整数 k(1≤k≤104), 表示命令的数量。后面 k 行,每行两个整数A B(1≤A≤B≤n),表示命令的内容。输出格式:输出一个整数,n个瓶子所装金币的中位数是多少。保证 n 为奇数,中位数是唯一的。输入样例:745 52 44 63 5输出样例:1样例解释:完成k次操作后,空瓶里金币的数量为[0,1,2,3,3,1,0]。 即[0,0,1,1,2,3,3],其中位数为1。
#include <iostream>#include <algorithm>using namespace std;const int N = 1e6+5;int c[N], n, m, y[N];int s, e;int main(){ cin >> n >> m; while (m--) { cin >> s >> e; c[s]++; c[e+1]--; } for ( int i = 1; i <= n; ++i ) { y[i] = y[i-1] + c[i]; } sort(y+1, y+n+1);// for ( int i = 1; i <= n; ++i ) {// cout << y[i] << " ";// }// cout << endl; cout << y[(n>>1)+1] << endl; return 0;}
在n*n的矩阵中,初始元素全为0,对它进行m次操作,求操作完成后的矩阵;输入格式第一行,两个正整数 n,m。意义如题所述。接下来 m 行,每行两个坐标 (x1,y1)和 (x2,y2),代表一次操作,左上角是 (x1,y1)右下角是 (x2,y2),对这个区域全部加一。输出矩阵输入5 32 2 3 33 3 5 51 2 1 4输出0 1 1 1 00 1 1 0 00 1 2 1 10 0 1 1 10 0 1 1 1
#include <iostream>using namespace std;int n, m;int q[1002][1002], answer[1002][1002];int main(){ int x1, y1, x2, y2; cin >> n >> m; for ( int i = 1; i <= m; ++i ) { cin >> x1 >> y1 >> x2 >> y2; for ( int i = x1; i <= x2; ++i ) { q[i][y1] += 1; q[i][y2+1] -= 1; } } int sum; for ( int i = 1; i <= n; ++i ) { sum = 0; for ( int j = 1; j <= n; ++j ) { sum += q[i][j]; answer[i][j] = sum; } } for ( int i = 1; i <= n; ++i ) { for ( int j = 1; j <= n; ++j ) { cout << answer[i][j] << ' '; } cout << '\n'; } return 0;}
5.高等排序
归并排序:
对数组进行分组,然后将两个有序数组合并,这期间需要用到一个临时数组用来过渡。所以会有O(n)的空间复杂度,
先分再合!
输入:
6
3 2 6 5 9 4
输出:
2 3 4 5 6 9
要求使用归并排序
先分:(如果分的组只有一个元素了,则默认就是有序的)
见如下图片:
再合
见如下图片:
合并两个有序数组:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 105;
int a[N], b[N], c[220];
int n, m;
int main()
{
cin >> n;
for ( int i = 0; i < n; ++i ) {
cin >> a[i];
}
cin >> m;
for ( int i = 0; i < m; ++i ) {
cin >> b[i];
}
//保证两个数组有序
sort(a, a+n), sort(b, b+m);
//合并
int i = 0, j = 0, k = -1;
while ( i < n && j < m ) {
if ( a[i] <= b[i] ) {
c[++k] = a[i];
++i;
}
else {
c[++k] = b[j];
++j;
}
}
while ( i < n ) {
c[++k] = a[i];
++i;
}
while ( j < m ) {
c[++k] = b[j];
++j;
}
//输出:
cout << "a数组:";
for ( int i = 0; i < n; ++i ) {
cout << a[i] << ' ';
}
cout << '\n';
cout << "b数组:";
for ( int i = 0; i < m; ++i ) {
cout << b[i] << ' ';
}
cout << '\n';
cout << "c数组:";
for ( int i = 0; i < n+m; ++i ) {
cout << c[i] << ' ';
}
cout << '\n';
return 0;
}
完整代码:
#include <iostream>using namespace std;const int N = 10e7;int num[N];//归并排序static int tmp[N];void merge_sort( int q[], int l, int r ){ if ( l >= r ) return; int mid = l + r >> 1; merge_sort(q, l, mid); merge_sort(q, mid + 1, r ); int k = 0, i = l, j = mid + 1; while ( i <= mid && j <= r ) { if ( q[i] <= q[j] ) { tmp[k++] = q[i++]; } else { tmp[k++] = q[j++]; } } while ( i <= mid ) tmp[k++] = q[i++]; while ( j <= r ) tmp[k++] = q[j++]; for ( i = l, j = 0; i <= r; ++i, ++j ) { q[i] = tmp[j]; }}int main(){ int n; cin >> n; for ( int i = 0; i < n; ++i ) { cin >> num[i]; } //归并排序 merge_sort(num, 0, n-1); for ( int i = 0; i < n; ++i ) { cout << num[i] << ' '; } cout << '\n'; return 0;}
综合归并,快排:
#include <iostream>
using namespace std;
const int N = 10e7;
int num[N];
//快速排序
void quick_sort( int q[], int l, int r )
{
if ( l >= r ) return;
int i = l, j = r, x = q[l + r >> 1];
while ( i < j ) {
while ( i < j && q[i] < x ) {
++i;
}
while ( i < j && q[j] > x) {
--j;
}
if ( i < j ) {
swap(q[i], q[j]);
}
}
quick_sort(q, l, j);
quick_sort(q, j + 1, r);
}
//归并排序
static int tmp[N];
void merge_sort( int q[], int l, int r )
{
if ( l >= r ) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r );
int k = 0, i = l, j = mid + 1;
while ( i <= mid && j <= r ) {
if ( q[i] <= q[j] ) {
tmp[k++] = q[i++];
}
else {
tmp[k++] = q[j++];
}
}
while ( i <= mid ) tmp[k++] = q[i++];
while ( j <= r ) tmp[k++] = q[j++];
for ( i = l, j = 0; i <= r; ++i, ++j ) {
q[i] = tmp[j];
}
}
int main()
{
int n;
cin >> n;
for ( int i = 0; i < n; ++i ) {
cin >> num[i];
}
//快速排序
//quick_sort(num, 0, n-1);
//归并排序
merge_sort(num, 0, n-1);
for ( int i = 0; i < n; ++i ) {
cout << num[i] << ' ';
}
cout << '\n';
return 0;
}
END
6.搜索思考题
搜索那一讲的思考题:参考代码,有更好的那自然是更好
#include <iostream>
using namespace std;
/*
表示乘号放的位置
3#2#1#5#1#2#5#4#6#9#7 #表示可以放的位置(1-10)
1 2 3 4 5 6 7 8 9 10
location[i]表示第i个乘号放的位置
location[1]=2表示第一个称号放在2号位
这里我想到一个提高速度的办法,就是这四个乘号的位置,可以看出第1,第2...第4个乘号的位置序号
是递增的,所以第一个乘号最多只能放在7号位,你这么聪明肯定能想通的
*/
int location[5];
int loca[5];
/*
一个位置只能放一个乘号,所以需要把放了
乘号的位置标记起来,例如book[1]=true表示1号位放了乘号了,
就不能再放了
*/
bool book[11]={false};
long long answer;
char a[] = "32151254697";
//now表示现在放第几个乘号
//now_index表示从当前位置序号开始放乘号
//因为上面说了,四个乘号的位置序列是递增的
void dfs( int now , int now_index)
{
//上面解释过了
if ( location[1] > 7 ) {
return ;
}
//放第五个乘号了,但是只有四个,所以放第五个的
//时候,就表示放完了
if ( now >= 5 ) {
cout << "当前:";
//计算结果,四个乘号分成了五个部分
long long tempans = 1, num, prevover = 0;
for ( int k = 1; k <= 4; ++k ) {
num = 0;
for ( prevover; prevover < location[k]; ++prevover ) {
num = num * 10 + a[prevover] - '0';
}
cout << num << '*';
tempans *= num;
}
//算第五个数
num = 0;
for ( prevover; prevover < 11; ++prevover ) {
num = num * 10 + a[prevover] - '0';
}
cout << num << '\n';
tempans *= num;
if ( tempans > answer ) {
answer = tempans;
for ( int i = 1; i < 5; ++i ) {
loca[i] = location[i];
}
}
return ;
}
//枚举当前乘号放的位置
for ( int i = now_index; i <= 10; ++i ) {
//如果当前位置没有放乘号,那么我们就放一个乘号
if ( !book[i] ) {
//标记起来
book[i] = true;
location[now] = i;//第now个乘号放的位置存起来
dfs(now+1, i+1);//递归下一步
book[i] = false; //递归结束,把状态还原,回溯的重要一步
}
}
}
int main()
{
int k = 1;
dfs(1, 1);
for ( int i = 0; i < 12; ++i ) {
cout << a[i];
if ( i+1 == loca[k] ) {
cout << '*';
++k;
}
}
cout << '\n';
cout << "最大值:" << answer << '\n';
return 0;
}
//for循环简单版,但是只适用于4个乘号#include<iostream>#include<bits/stdc++.h>using namespace std;int p(char *s,int b,int r) { int m = 0, i; for(i = b; i <= r; i++) m=10*m+s[i]-'0'; return m;}int main(void){ char s[]="32151254697"; long long sum = 1; for(int i = 0; i <= 6; i++) { for(int j = 1; j <= 7; j++) { for(int k = 2; k <= 8; k++) { for(int z = 3; z <= 9; z++) { if(i < j && j < k && k < z) { int a = p(s,0,i); int b = p(s,i+1,j); int c = p(s,j+2,k); int d = p(s,k+1,z); int e = p(s,z+1,10); long long m = a*b*c*d*e; if(sum < m) sum = m; } } } } } cout << sum << endl; return 0;}