C语言入门训练题之:寻找鞍点

一、简介。

哈喽哈喽,本人其实是一名初学c的小白(小菜鸡)。偶然遇到找鞍点这样一道题,觉得还挺有意思的,遂写下这篇blog,才疏学浅,还望大佬们多多指教~言归正传,鞍点就是矩阵中行最大,列最小的点。这道题直观上思路是很明显的——先找某一行中最大的点,然后遍历该点所在列,看看该点是否同时是列最小的点。不过实际实现中还是有不少问题的,一起来看一下吧!

p.s.本人比较啰啰嗦嗦(唉唉思维平庸是这样的),高手可以直接拉到最下面看完整的代码。

二、代码实现


#include <stdio.h>
void FindSpecialPoint(int m, int n);

int main(void) {
	int m, n;
	printf("几行几列?\n");
	scanf("%d%d", &m, &n);
	FindSpecialPoint(m, n);

}

搞一个函数 FindSpecialPoint,其实直接在main函数里写也行,这里封装成了一个函数,这个函数的功能就是输入数组,输出鞍点。

下面来到这个函数的本体(定义)部分~

void FindSpecialPoint(int m, int n) {
	printf("请输入数组:\n");
	int arr[m][n];
	int i, j,flag=0;
	int  min,max, k;//下面的步骤会说明这些个变量是干嘛的
    int col[n];

	for (i = 0; i < m; i++) {
		for (j = 0; j < n; j++) {
			scanf("%d", &arr[i][j]);
		}
	}

这是第一部分,即输入数组部分,通过两重循环即可实现——外层行遍历,每一行内列遍历。

然后是第二部分:找出每一行的最大值。思路也是很显然的——设一个max,max初始化为arr[i][0],即这一行的第一个元素,往后遍历该行,后一个比前一个大就把后一个赋给max。

for (i = 0; i < m; i++) {
		max = arr[i][0];//max 已经在最开头声明了,在循环内部声明的话则会是一个局部变量,非常难受
		
		for (j = 1; j < n; j++) {
			if (max < arr[i][j]) {
				
				max = arr[i][j];
			}
		}

看样子我们终于得到了一行的最大值。然而,有一个很棘手的问题是,当一行中的最大值不止一个的时候,这样的程序显然会遗漏很多东西(max只会“停在”第一个最大点处),但我们想要实现的是每个最大点都要判断它是不是同时是该列的最小点。

我们的想法是,再来一个数组,专门负责装一行中所有最大点所在列的列数。如下,我们用数组col来装:

for(h=0,k=0;h<n;h++)//h来控制遍历列的循环,一共n列嘛
		{
			if(arr[i][h]==max){//有一个等于max,就往col里装上他的列数
			col[k]=h;
			k++;//数组col下一个(比如说col[1])准备好,同时k还可以表明有几个最大点}
		}

我们再阐明清楚col的强大作用(笑)。现在第一个最大点所在列我们就能用col[0]来表示了!!!第二个最大点所在列就是col[1]!!!

总之,我们来到了第三步——判断这些个行最大点是否同时是所在列的最小点。

我们不妨把min初始化为max。当然也可以初始化成该列(行最大点所在列)的任意元素,只是后面的判断条件相应修改即可。

到底该怎么循环呢?我们既要遍历行最大点所在列的元素,看看max是否确实也是该列的最小值;同时我们的行最大点有可能不止一个,我们要遍历的列也就不止一列。那究竟要遍历多少列呢?哎,这个问题早就由我们的col数组解决啦!想一下,第一个行最大点所在列数装入了col[0],然后k++,k变成1......没错,正如前文所言,k已经告诉我们到底有几列要遍历了。

现在一切都清晰无比了——外层循环走一遍我们要遍历的列,内层就正常从上到下遍历列。如下:

min=max;
		for(j=0;j<k;j++)
		{
			for(h=0;h<m;h++){
			if(arr[h][col[j]]<min)
			break;	
			}
         if(h==m){
				printf("鞍点为arr[%d][%d]=%d\n",i,col[j],max);
                flag++;
			}
		}

有几点要说明一下:①j,h只是用来定循环范围的变量,所以重复使用他们(前面的循环用过j,h了)没什么关系。但是,i我们不能用在这里,因为i后面有大用——i告诉我们鞍点在第几行!!!(鞍点在第几列由col告诉我们了)当然,你也可以另外搞变量。

②这里break跳出的是离他最近的for循环。break的作用是显然的——你这一列只要有一个点比你这个min要小,那就没必要接着走完这一列了,跳出内层循环即可,进入到下一次外层循环,即另外一列。

③这一列全部循环完了的标志就是h==m,循环完了就说明min确实是这一列最小的点,bingo!这就是我们的鞍点啦!我们输出出来:鞍点在第i行(i是最外层行的大循环嘛),第col[j]列(不要忘了col装的是啥哦),值当然就是max(或者min)。

第四部分——最后一个问题:没有鞍点怎么办呢?

我们需要一个信号灯,它告诉系统什么灯代表什么情况。(比如说绿灯,一切正常;红灯,有内鬼,终止交易)我们一开始设置一个函数中的变量(意为只要在这个函数范围内,flag都畅通无阻,你可以在任意把flag叫过来,对他进行一系列操作(意味深),但出了函数就叫不出他了)并且初始化为0。在上面的代码中,若符合鞍点,flag会变成1。那么如果这么多遍循环后flag还是0,那就说明没有鞍点嘛。flag,旗帜,很形象吧。

总之,我们在最后加上:

if(flag==0)
	printf("没有鞍点");

注意:这个语句放在所有循环外面!(你可以想想放里面会发生什么)

太酷了!来测试一下程序吧!

以下是完整代码:

#include <stdio.h>
void FindSpecialPoint(int m, int n);

int main(void) {
	int m, n;
	printf("几行几列?\n");
	scanf("%d%d", &m, &n);
	FindSpecialPoint(m, n);

}

void FindSpecialPoint(int m, int n) {
	printf("请输入数组:\n");
	int arr[m][n];
	int i, j, flag = 0;
	int  min, max, k, h;
	int col[n];
	for (i = 0; i < m; i++) {
		for (j = 0; j < n; j++) {
			scanf("%d", &arr[i][j]);
		}
	}


	for (i = 0; i < m; i++) {
		max = arr[i][0];

		for (j = 1; j < n; j++) {
			if (max < arr[i][j]) {

				max = arr[i][j];
			}
		}
		for (h = 0, k = 0; h < n; h++) { //h来控制遍历列的循环,一共n列嘛
			if (arr[i][h] == max) { //有一个等于max,就往col里装上他的列数
				col[k] = h;
				k++;//数组col下一个(比如说col[1])准备好,同时k还可以表明有几个最大点}
			}
		}
		min = max;
		for (j = 0; j < k; j++) {
			for (h = 0; h < m; h++) {
				if (arr[h][col[j]] < min)
					break;
			}
			if (h == m) {
				printf("鞍点为arr[%d][%d]=%d\n", i, col[j], max);
				flag++;
			}
		}

	}
	if (flag == 0)
		printf("没有鞍点");
}

小结:这道题主要是对数组,循环和分支的考察。思路比较简单,细节稍微多一点。对于我这种大一糊里糊涂选了网安专业,还毫无基础的小菜鸡来说已经是难题了,希望大佬们多多指教😋另外就是本人现在学得非常迷茫,希望有过来人指点一二,感激不尽!

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值