根据zq的安排,已经连续两天下午和晚上一直写题,看题了。
递归和递推
先看最简单的
这个链接里面有寒假接触的递归和递推
再有就是侯大佬用斐波那契数列举的例子。
先是递归
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int main()
{
int i;
int a[50]={0};
a[0] = 1;
a[1] = 1;
for(i = 2; i < 15; i ++){
a[i] = a[i - 1] + a[i - 2];
}
for(i = 0; i < 15; i ++)printf("%d ",a[i]);
return 0;
}
之后再是递归的做法
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int a[50] = {1,1};
void w(int n)
{
if(n == 15)return ;
else{
a[n] = a[n - 1] + a[n - 2];
}
w(n + 1);
}
int main()
{
w(2);
for(int i = 0; i < 15; i ++)printf("%d ",a[i]);
return 0;
}
再下边就是没有人性的acwing
说好是递归跟递推的,(ಥ﹏ಥ)结果我莫名接触了我一点都不会的dfs还有一点点的哈希。难哭我。
先做92,94,93,717,1208;95写完之后写116,1209最后写
对应蓝书的p11-p16
递归实现指数型枚举
原题链接
先跟上蓝书上的代码
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int> chosen;
void calc(int x){
if (x == n + 1){
for(int i = 0; i < chosen.size(); i ++)
printf("%d ",chosen[i]);
puts("");
return;
}
//“不选x”分支
calc(x + 1);
//“选x”分支
chosen.push_back(x);//在数组的最后添加一个元素
calc(x + 1);
chosen.pop_back();//在数组的最后删除一个元素
}
int main()
{
cin >> n;
calc(1);
}
走一遍程序,当n=3时,进行calc(1),再进行calc(2),再进行calc(3),再进行calc(4)此时x=4=3+1=n+1,此时的chose为空,输出一个空格,之后就是按照步骤走了。
这可能是我唯一一个看得明白的代码。
递归实现排列型枚举
原题链接
下边先是蓝书的代码
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int n;
int order[20];
bool chosen[20];
void calc(int k){
if (k == n + 1){
for(int i = 1; i <= n; i ++)
printf("%d ",order[i]);
puts("");
return;
}
for(int i = 1; i <= n; i ++){
if(chosen[i])continue;
order[k] = i;
chosen[i] = 1;
calc(k + 1);
chosen[i] = 0;
order[k] = 0;
}
}
int main(){
cin >> n;
calc(1);
}
之后是雅文写的代码,当雅文告诉我dfs的时候我的内心是十分抗拒的,拒绝学习超纲内容。
#include <iostream>
using namespace std;
int a[15], book[15];
int n;
void dfs(int u)
{
if(u == n){
for(int i = 0; i < n; i ++){
cout << a[i] << " ";
}
cout << endl;
return ;
}
for(int i = 1; i <= n; i ++){
if(!book[i]){
book[i] = 1;
a[u] = i;
dfs(u + 1);
a[u] = 0;
book[i] = 0;
}
}
}
int main()
{
cin >> n;
dfs(0);
return 0;
}
再下边就是y总写的代码了
#include<iostream>
#include<vector>
using namespace std;
int n;
vector<int> path;
void dfs(int u,int state)
{
if(u == n){
for(auto x : path) cout << x << ' ';
cout << endl;
return ;
}
for(int i = 0; i < n; i ++){
if(!(state >> i & 1)){
path.push_back(i + 1);
dfs(u + 1,state | (1 << i));
path.pop_back();
}
}
}
int main()
{
cin >> n;
dfs(0,0);
return 0;
}
之后就会有一些相关的知识点
auto声明(自动)
让编译器根据初始值类型直接推断变量的类型,例如:
auto x = 100;//x是int变量
auto y = 1.5;//y是double变量
基于范围的for循环
遍历数组中的每一个元素时使用会比较简单。比如想要输出数组arr中的每一个值,可以用以下方法输出:
int arr[4] = {0,1,2,3};
for(auto i : arr)
cout << i << endl;
关于&和|的知识点
&和|都是二进制的按位操作符
&:二进制“与”(都为1时,结果为1,否则是0),比如1010&1011 = 1010, 1010&1000 = 1000.
|:二进制“或”(有1时,结果为1,都是0时,结果为0),比如1010 | 1011 = 1011,1010 | 1000 = 1010.
递归实现组合型枚举
原题链接
先是蓝书的代码,它的输出是倒过来了,并且因为我的一知半解,我并不知道怎么改这个代码
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int n,m;
vector<int> chosen;
void calc(int x){
if (chosen.size() > m || chosen.size() + (n - x + 1) < m){
return;
}
if (x == n + 1){
for(int i = 0; i < chosen.size(); i ++)
printf("%d ",chosen[i]);
puts("");
return;
}
calc(x + 1);
chosen.push_back(x);
calc(x + 1);
chosen.pop_back();
}
int main()
{
cin >> n >> m;
calc(1);
}
之后再是一个雅文的代码
#include <iostream>
using namespace std;
int n, m;
int a[105], book[105];
void dfs(int u)
{
if(u == m){
for(int i = 0; i < m; i ++){
cout << a[i] << " ";
}
cout << endl;
return;
}
for(int i = 1; i <= n; i ++){
if(!book[i] && a[u - 1] < i){
a[u] = i;
book[i] = 1;
dfs(u + 1);
book[i ] = 0;
a[u] = 0;
}
}
}
int main()
{
cin >> n >> m;
dfs(0);
return 0;
}
再跟一个y总的代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
const int N = 30;
int n,m;
int path[N];
void dfs(int u, int start)
{
if(u > m)
{
for(int i = 1; i <= m; i ++)
printf("%d ",path[i]);
puts("");
}
else{
for(int i = start; i <= n; i ++){
path[u] = i;
dfs(u + 1, i + 1);
path[u] = 0;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
dfs(1,1);
return 0;
}
其实代码的思路都是好懂的,但是当你自己写的时候,你不一定能有思路就是了。
二分与前缀和
做题顺序789、790、795、796、730、1227、1230、99、1221
1221(暴力或二分或哈希)
二分
789、790、730、1227(二分)
先写上寒假二分的点
蓝书上二分对应了p25,虽然没有看太明白它说的一些点,但是学到了几个函数
lower_bound(起始地址,结束地址,要查找的数值)
返回的是数值第一个出现的位置
upper_bound(起始地址,结束地址,要查找的数值)
返回的是第一个大于待查找数值出现的位置
binary_search(起始地址,结束地址,要查找的数值)
返回的是是否存在这么一个数,是一个bool值
蓝书上写
s.lower_bound(x)
查找>=x的元素中最小的一个,并返回指向该元素的迭代器。
s.upper_bound(x)
查找>x的元素中最小的一个,并返回指向该元素的迭代器。
数的范围
原题链接
之后就是我写的代码了,思路什么的都很简单,但是zq说也要学会手写二分
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
//#include<vector>
using namespace std;
int main()
{
int n,q,l,r;
scanf("%d%d",&n,&q);
int a[n+10]={0};
for(int i = 0; i < n; i ++){
scanf("%d",&a[i]);
}
for(int i = 0; i < q; i ++){
int b;
scanf("%d",&b);
if(binary_search(a ,a + n, b)){
printf("%d %d\n",lower_bound(a ,a + n, b)-a,upper_bound(a ,a + n, b)-a-1);
}
else
printf("-1 -1\n");
b = 0;
}
return 0;
}
之后就有了手写二分
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,q,x,a[10010];
scanf("%d%d",&n,&q);
for(int i = 0; i < n; i ++)scanf("%d",&a[i]);
while(q--){
scanf("%d",&x);
int l = 0, r = n - 1;
while(l < r){
int mid = l + r >> 1;
if(a[mid] < x) l = mid + 1;
else
r = mid;
}
if(a[l] != x){
printf("-1 -1\n");
continue;
}
int l1 = l, r1 = n;
while(l1 + 1 < r1){
int mid = l1 + r1 >> 1;
if(a[mid] <= x) l1 = mid;
else
r1 = mid;
}
printf("%d %d\n",l,l1);
}
return 0;
}
之后的例题再去acwing找吧,思路差不都都是那个思路
前缀和
对应蓝书的p21,虽然蓝书的题解不是很平易近人
下面跟一个比较友善的讲解什么是前缀和
链接
前缀和(一维前缀和)
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int n,m;
cin >> n >> m;
int a[n+10]={0},b[n+10]={0};
for(int i = 1; i <= n; i ++){
cin >> a[i];
b[i] = b[i - 1] + a[i];
}
for(int i = 0; i < m; i ++){
int x,y;
cin >> x >> y;
printf("%d\n",b[y]-b[x-1]);
}
return 0;
}
子矩阵的和(二维前缀和)
原题链接
友善题解
一开始是不太清楚要怎么计算二维的前缀和的,看了题解之后清晰了不少,所以鄙人代码和题解的基本一样
#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int a[N+10][N+10],b[N+10][N+10];
int main()
{
int n,m,q;
cin >> n >> m >> q;
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= m; j ++){
scanf("%d",&a[i][j]);
b[i][j] = b[i-1][j] + b[i][j-1] + a[i][j] - b[i - 1][j - 1];
}
}
for(int i = 0; i < q; i ++){
int x1,y1,x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
printf("%d\n",b[x2][y2]-b[x2][y1-1]-b[x1-1][y2]+b[x1-1][y1-1]);
}
return 0;
}