第一题
这个题目主要是希望大家掌握下常见的数组遍历的流程,对于数组遍历,一般情况下下都是这样的
int len=n;//通过事先指定或者计算得到数组长度
for(int i=0;i<len;i++)
{
//执行需要做的判断
}
这个地方首先是i
这个变量我的习惯是随用随定义而不是在for循环外部定义一个变量,这样做的好处就在于条理清晰,明确这个变量仅在循环中使用,使用完毕之后会自动销毁,不容易写出死循环
回归本题,一个很常见的思路就是先确定好x
在数组中首次出现和末次出现的下标Min
和Max
,通常我会将Min
设置为数组长度n
,Max
设置为-1
,因为有个很显而易见的事情是数组中元素的下标是0~n-1,这样只需要让每次找到值为x
的元素时(假设对应下标为index
),令
M
i
n
=
m
i
n
(
M
i
n
,
i
n
d
e
x
)
Min=min(Min,index)
Min=min(Min,index)
M
a
x
=
m
a
x
(
M
a
x
,
i
n
d
e
x
)
Max=max(Max,index)
Max=max(Max,index)最后判定下Min
是否为n
,Max是否为-1
即可知道数组中是否存在对应的元素x
。
常见的寻找最大最小的方法是先确定好数据的取值范围,令Min大于最大值,Max小于最小值,这样无须其他判断,只需要遍历并且调用min和max函数就可以得到最大最小值
#include<iostream>
using namespace std;
int main()
{
int n,x,i,first=-1,last=-1;
cin >>n;
int a[n];
for(i=0;i<n;i++){
cin >>a[i];
}
cin >>x;
for(i=0;i<n;i++){
if(a[i]==x){
if(first==-1){
first=i;//first只赋值1次,每找到一次目标值更新一次last
}
last=i;
}
}
cout <<first<<' '<<last;
return 0;
}
第二题
这个题目有多种解题思路
- 一种常见方法是直接行优先遍历二维数组,找出每行数组中
1
的个数并记录,这样遍历从而找到1
的个数最多的那一行,这个是适合大多数题目的解法 - 另一种思路就是更加简单,针对这题的特征进行分析,输入的数组中,每一行都是
1
在前,0
在后,换言之,如果按照从右往左,从上往下进行遍历,数组中第一个元素1
所在的行下标就是我们需要找到的目标,(因为要找1
最多,并且0-1数组中每一行的元素个数都相等,所以1
最多的那一行最后一个1
必然更加靠近数组的右边界,从而自然可以想到从右往左遍历)
#include<iostream>
using namespace std;
int main()
{
int n,i,j,flag=0;
cin >>n;
int a[n][n];//b[n]存放每一行1的个数
for(i=0;i<n;i++){
for(j=0;j<n;j++){
cin >>a[i][j];
}
}
for(j=n-1;j>=0;j--){
for(i=0;i<n;i++){
if(a[i][j]==1){
cout << i;
flag=1;
break; //找到1最多的行跳出内层循环
}
}
if(flag==1)
break;//跳出外层循环
}
return 0;
}
第三题
这种题目主要锻炼大家的理解能力,题目本身的意思很好理解,就需要定位最后的结果为啥出错,从给的示例结合代码很容易发现输出结果的第二行数据错误,其他的地方没有问题,这说明啥?结合这一段代码
for(i = m-1; i>0; i--){
for(j = 0; j<n; j++){
mat[(i+1)%m][j]=mat[i][j];
}
}
很快我们就能发现错误原因在于第一行的数据在循环最开始就被错误覆盖了,同时在循环最后只执行到将下标为1
的行移动到下标为2
的行就结束了,理清为啥会错就很简单了,预先将第一行的数据保存,在原程序移动完成之后将第一行的数据覆盖回去即可,再将不符合题意的输出去除即可完成此题
#include <iostream>
using namespace std;
const int MAX_SIZE = 10;
int main(){
int i, j, m, n;
int mat[MAX_SIZE+1][MAX_SIZE];
cin>>m>>n;
int tmp[n]={0};
for(i = 0; i<m; i++){
for(j = 0; j<n; j++){
cin>>mat[i][j];
if(i==m-1)
{
tmp[j]=mat[i][j];
}
}
}
for(i = m-2; i>=0; i--){
for(j = 0; j<n; j++){
mat[i+1][j]=mat[i][j];
}
}
for(j=0;j<n;j++)
{
mat[0][j]=tmp[j];
}
for(i = 0; i<m; i++){
for(j = 0; j<n; j++){
cout<<mat[i][j]<<'\t';
}
cout<<endl;
}
return 0;
}
第四题
这个题目有个很容易陷入的思维陷阱就是前面几个题目都是要找对应元素的下标,而这题虽然也是输出对应元素下标,但是其实本身应该是从元素的值的角度上进行思考,归根结底,就在于鞍点的定义导致了一行之中可能存在多个鞍点,最简单的例子,假如一行全部都是1
,从题目给的定义可以得知这一行全部都是鞍点,所以很多同学所想的找到行最大值的下标的方法往往无法得出正确答案(不是说做不了,但是很麻烦,没必要)
正确做法是
- 首先行优先遍历,找出每一行最大元素的值并且存储
- 接下来列优先遍历,找出每一列最小元素的值并且存储
- 最后对每个元素进行遍历,判定它是否符合鞍点的定义(遍历过程你自然会知道该元素对应的行坐标和列坐标,从而得出对应的行最大值和列最小值)
也有些同学的做法是直接遍历数组,对每个元素再次按照定义判定当行/列的元素是否满足鞍点定义,不是说不能做,但是这样会有三个循环的嵌套,一般情况下尽量避免写这种程序,容易出问题而且往往效率低下
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int main()
{
int m,n;
cin >> m>>n;
int nums[m][n]={0};
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
cin >> nums[i][j];
}
}
int maxValue=-1;
int rowMax[m]={0};
int minValue=100;
int columnMin[n]={0};
bool hasFound=false;
bool isColumnMin=false;
//找到行最大值并存储
for(int i=0;i<m;i++)
{
maxValue=-1;
for(int j=0;j<n;j++)
{
maxValue=max(maxValue,nums[i][j]);
}
rowMax[i]=maxValue;
}
//找到列最小值并存储
for(int j=0;j<n;j++)
{
minValue=100;
for(int i=0;i<m;i++)
{
minValue=min(minValue,nums[i][j]);
}
columnMin[j]=minValue;
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(nums[i][j]==rowMax[i]&&nums[i][j]==columnMin[j])
{
hasFound=true;
cout <<"mat["<<i<<"]["<<j<<"]="<<nums[i][j]<<endl;
}
}
rowMax[i]=maxValue;
}
if(!hasFound)
{
cout <<"Not Found";
}
return 0;
}
第五题
这个题目想拿分数还是很简单的,直接调用sort函数就好,这个题主要和之前题目中的冒泡排序算法进行对比,网上已经有很生动的解析了,这个地方给出链接十种常见排序算法动画,里面后续算法的会用到一些特殊结构比如堆排序,这种东西学有余力的同学了解即可,后续课程中的数据结构那一门(如果有的话)会专门讲这些算法,现在阶段了解下插入,冒泡这两个基本上够用了。
#include <iostream>
using namespace std;
int main()
{
int n=0;
cin >> n;
int nums[n];
for(int i=0;i<n;i++)
{
cin >>nums[i];
}
int tmp=0;
int j=0;
for(int i=1;i<n;i++)
{
for(j=0;j<i;j++)
{
if(nums[j]>nums[i])
{
break;
}
}
if(j!= i)
{
tmp=nums[i];
for(int z=i;z>j;z--)
{
nums[z]=nums[z-1];
}
nums[j]=tmp;
}
}
for(int i=0;i<n;i++)
{
cout <<nums[i]<<" ";
}
return 0;
}
第六题
这个题目基本上算是很经典的动态规范的算法题目了,动态规划实际上就是利用上一步已经得到的数据计算下一步的一种算法,类比一下可以参考数学归纳法,核心思想是我已经得到了之前的一些数据,我该怎么获取下一步的数据,就比如这题,假定数据存放在如下的数组中
依照题目的意思就是只允许往正下方或者右下方1个格子移动。依照动态规划的思想,我们可以设置一个两维数组 f,f[i,j] 表示从最高点(最高点为第 1 层)到达第 i 层第 j 个位置时经过路径数字的最大和,初始化的时候将原本数组元素填充进去,这样由于移动方式被限制,我们就可以得到对应的动规方程
f
[
i
+
1
,
j
]
+
=
M
a
x
(
f
[
i
,
j
−
1
]
,
f
[
i
,
j
]
)(
1
≤
i
≤
r
−
1
,
1
≤
j
≤
i
)
f[i+1,j]+=Max(f[i,j-1],f[i,j])(1≤i≤r-1,1≤j≤i)
f[i+1,j]+=Max(f[i,j−1],f[i,j])(1≤i≤r−1,1≤j≤i)也就是当前元素对应的最大值必然是(左上角得到的最大值,正上方得到的最大值)中较大的一方与本身数组元素的值之和,另外再考虑边界情况即可得到对应的算法,边界就是最上面一行和最左边一列的初始值都为0
,因为算法没有考虑到i==j==0
的情况,所以f[i,j]需要多加一个边界
这块不要求完全掌握,只是给学有余力的同学了解下动态规划算法
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int n=0;
cin >> n;
int nums[n][n]={0};
for(int i=0;i<n;i++)
{
for(int j=0;j<=i;j++)
cin >>nums[i][j];
}
//多出的部分是边界情况
int dp[n+1][n+1]={0};
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
dp[i][j]=0;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+nums[i-1][j-1];
}
}
//二维数组每一行都可以当一维数组来排序,但是列不行
sort(dp[n],dp[n]+n+1);
cout << dp[n][n];
return 0;
}