🐾倒计时4天,冲冲冲!!!🐾
1.整除序列
-
题目
有一个序列,序列的第一个数是 n,后面的每个数是前一个数整除 2,请输出这个序列中值为正数的项。
输入格式
输入一行包含一个整数 n。
输出格式
输出一行,包含多个整数,相邻的整数之间用一个空格分隔,表示答案。
数据范围
1≤n≤10 18 ^{18} 18
输入样例:
20
输出样例:
20 10 5 2 1
-
第一次 AC 0%
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() { ll n; cin>>n; while(n) { cout<<n/2<<' '; n/=2; } return 0; }
-
第二次
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() { ll n; cin>>n; cout<<n<<' '; //原数需要输出 while(n>1) //1/2=0,不能要,而且通过样例可以看出来每一个数组最后都是 1 { cout<<n/2<<' '; n/=2; } return 0; }
-
反思
- 特殊数值,特判
- 考试的时候,没有检测,只能自己写几个样例,现在开始,自己造样例,确定无误之后在 提交
2.走方格
-
题目
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第 n 行,从左到右依次为第 1 至第 m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 行第 1 列,要走到第 n 行第 m 列。
只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
输入格式
输入一行包含两个整数 n,m。
输出格式
输出一个整数,表示答案。
数据范围
1≤n,m≤30
输入样例1:
3 4
输出样例1:
2
输入样例2:
6 6
输出样例2:
0
-
第一次 AC 8/10
#include<bits/stdc++.h> using namespace std; typedef pair<int,int> PII; const int N=40; int ans; int n,m; int g[N][N]; bool st[N][N]; void dfs(int x,int y) { if(x==n&&y==m) { ans++; return; } //两种方案,向右,向下 if(x%2==0&&y%2==0||(x>n||y>m)) //注意处理 边界条件(我都说累了) return; if(!st[x][y]) { st[x][y]=1; dfs(x+1,y); dfs(x,y+1); st[x][y]=0; } } int main() { cin>>n>>m; dfs(1,1); cout<<ans; return 0; }
-
第二次 AC 9/10
#include<bits/stdc++.h> using namespace std; typedef pair<int,int> PII; const int N=40; int ans; int n,m; int g[N][N]; bool st[N][N]; void dfs(int x,int y) { if(x%2==0&&y%2==0||(x>n||y>m)) //把这个提在前面 return; if(x==n&&y==m) { ans++; return; } //两种方案,向右,向下 if(!st[x][y]) { st[x][y]=1; dfs(x+1,y); dfs(x,y+1); st[x][y]=0; } } int main() { cin>>n>>m; dfs(1,1); cout<<ans; return 0; }
-
题解
#include <bits/stdc++.h> using namespace std; int n, m; int f[31][31]; // 记忆化数组 int dfs(int x, int y) // 搜索点 (x, y),并返回从点 (x, y) 开始,能到点 (n, m) 的路径数量 { if (x & 1 || y & 1) { if (f[x][y]) return f[x][y]; // 如果该点已经被搜索过,那么不再处理 // 否则说明没搜索过,需要搜索一遍 if (x < n) f[x][y] += dfs(x + 1, y); if (y < m) f[x][y] += dfs(x, y + 1); } return f[x][y]; // 最后返回 f[x][y] 即可。如果 x, y 都是偶数,那么 f[x][y] 就没被处理过,必然为 0,可以不特判。 } int main() { scanf("%d%d", &n, &m); f[n][m] = n & 1 || m & 1; // 这里要特判下 n, m 是否都为偶数 printf("%d\n", dfs(1, 1)); return 0; }
-
反思
暴搜没有把最后一个数据过了,TLE,我最爱的暴搜 TvT
看了这个题解,虽然没有学习过记忆化搜索,但他看起来好酷
get 新技能
3.前缀和
-
题目
输入一个长度为 n 的整数序列。
接下来输入 m 个操作,每个操作包含三个整数 l,r,c 表示将序列中 [ l , r ] 之间的每个数加上 c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数序列。
接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。
输出格式
共一行,包含 n 个整数,表示最终序列。
数据范围
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000 -
第一次 AC 100%
#include<bits/stdc++.h> using namespace std; const int N=100010; int n,m; int a[N],b[N]; void insert(int l,int r,int c) { b[l]+=c; b[r+1]-=c; } int main() { cin>>n>>m; for(int i=1;i<=n;i++) { cin>>a[i]; insert(i,i,a[i]); //这里注意,是 a[i] } while(m--) { int x,y,c; cin>>x>>y>>c; insert(x,y,c); } for(int i=1;i<=n;i++) { a[i]=a[i-1]+b[i]; cout<<a[i]<<' '; } return 0; }
4.差分矩阵
-
题目
输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1) 和 (x2,y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上 c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数 n,m,q 。
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c,表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1≤n,m≤1000,
1≤q≤100000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤c≤1000,
−1000≤矩阵内元素的值≤1000−1000≤矩阵内元素的值≤1000输入样例:
3 4 3 1 2 2 1 3 2 2 1 1 1 1 1 1 1 2 2 1 1 3 2 3 2 3 1 3 4 1
输出样例:
2 3 4 1 4 3 4 1 2 2 2 2
-
第一次 AC 100%
#include<bits/stdc++.h> using namespace std; const int N=1010; int n,m,q; int a[N][N],b[N][N]; void insert(int x1,int y1,int x2,int y2,int c) { b[x1][y1]+=c; b[x1][y2+1]-=c; b[x2+1][y1]-=c; b[x2+1][y2+1]+=c; } int main() { cin>>n>>m>>q; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { cin>>a[i][j]; insert(i,j,i,j,a[i][j]); //利用函数构造差分数组 } while(q--) { int x1,y1,x2,y2,c; cin>>x1>>y1>>x2>>y2>>c; insert(x1,y1,x2,y2,c); } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1]; //求前缀和 cout<<b[i][j]<<' '; } puts(" "); //输出格式注意 } return 0; }
-
反思
差分和前缀和,利用图形,联想,记公式
公式中:前缀和 -1 / 差分 +1
5.翻硬币
-
题目
小明正在玩一个“翻硬币”的游戏。
桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零)。
比如,可能情形是:
**oo***oooo
如果同时翻转左边的两个硬币,则变为:
oooo***oooo
现在小明的问题是:如果已知了初始状态和要达到的目标状态,每次只能同时翻转相邻的两个硬币,那么对特定的局面,最少要翻动多少次呢?
我们约定:把翻动相邻的两个硬币叫做一步操作。
输入格式
两行等长的字符串,分别表示初始状态和要达到的目标状态。
输出格式
一个整数,表示最小操作步数
数据范围
输入字符串的长度均不超过100。
数据保证答案一定有解。输入样例1:
********** o****o****
输出样例1:
5
输入样例2:
*o**o***o*** *o***o**o***
输出样例2:
1
-
第一次 WA
#include<bits/stdc++.h> using namespace std; int ans; string a,b; void f(char c) //没有使用引用 { if(c=='o') c='*'; else c='o'; } int main() { cin>>a>>b; for(int i=0;i<a.size();i++) { if(a[i]!=b[i]) { f(a[i]); f(a[i+1]); ans++; } } cout<<ans; return 0; }
-
第二次 AC 100%
#include<bits/stdc++.h> using namespace std; int ans; string a,b; void f(char &c) //这里引用 { if(c=='o') c='*'; else c='o'; } int main() { cin>>a>>b; for(int i=0;i<a.size();i++) { if(a[i]!=b[i]) { f(a[i]); f(a[i+1]); ans++; } } cout<<ans; return 0; }
-
反思
函数来改变某个变量,记得使用 引用
函数最基础的知识,都忘记了