题目描述
给定一个n*n的矩阵(3<=n<=100,元素的值都是非负整数)。通过n-1次实施下述过程,可把这个矩阵转换成一个1*1的矩阵。每次的过程如下:
首先对矩阵进行归零:即对每一行(或一列)上的所有元素,都在其原来值的基础上减去该行(或列)上的最小值,保证相减后的值仍然是非负整数,且这一行(或列)上至少有一个元素的值为0。
然后对矩阵进行消减:即把n*n矩阵的第二行和第二列删除(如果二维数组为a[][],则删除的是a[1][1]所在的行和列),使之转换为一个(n-1)*(n-1)的矩阵。
下一次过程,对生成的(n-1)*(n-1)矩阵实施上述过程。显然,经过n-1次上述过程, n*n的矩阵会被转换为一个1*1的矩阵。
请求出每次消减前a[1][1]值之和。
关于输入
第一行是一个整数n。
其后是n个n*n的矩阵。
每个矩阵占n行,每行有n个正整数,每个整数间用空格分隔。
关于输出
输出为n行,每行上的整数为对应矩阵归零消减过程中,每次消减前a[1][1]值之和。
例子输入
3 1 2 3 2 3 4 3 4 5 1 2 3 5 4 2 9 4 5 1 2 3 5 4 2 9 5 4
例子输出
0 2 1
分析
通过读题,可以发现,本题的总体思路就是定义一个函数来实现矩阵的归零、消减以及a[1][1]的求和,然后将该函数不断递归直到矩阵仅剩1行1列。笔者认为,本题的重点在于如何理解题目中给的归零与消减这两个操作。值得注意的是,在归零步骤中,并不是把每一行都减去其中的最小值就可以的,我们还需要判断在完成以上操作后是否能实现矩阵的每行每列都至少有一个元素的值为0,如果不满足条件,则我们还需要对每一列也进行归零操作。
下面给出一种较为直接的方法:
#include<iostream>
using namespace std;
int n;
int b[100][100];//定义一个二维数组来表示矩阵
int i, j, k;
int sum(int a[100][100], int l, int s) {//定义sum函数来求a[1][1]的和,其中,a[100][100]表示即将进行操作的矩阵,l表示矩阵的行(列)数,s表示当前a[1][1]的和
int s1;
if (l == 1) {
return s;
}//当l=1时,矩阵只剩1行1列,此时即可返回s的值
else {
int i, j, k;
int min;
for (i = 0; i < l; i++) {//注意这边矩阵的行(列)数为l而不是n
min = a[i][0];
for (j = 0; j < l; j++) {
if (a[i][j] == 0) {
min = 0;
break;
}//如果min=0,则不会再有比min更小的非负数,此时可以不用再继续比较
else if (a[i][j] < min) {
min = a[i][j];
}
}
if (min != 0) {//只有当min != 0时有必要进行操作
for (j = 0; j < l; j++) {
a[i][j] -= min;
}
}
}//以上步骤用于对矩阵的每一行进行归零操作
for (j = 0; j < l; j++) {
min = a[0][j];
for (i = 0; i < l; i++) {
if (a[i][j] == 0) {
min = 0;
break;
}
else if (a[i][j] < min) {
min = a[i][j];
}
}
if (min != 0) {
for (i = 0; i < l; i++) {
a[i][j] -= min;
}
}
}//以上步骤用于对矩阵的每一列进行归零操作
s1 = s + a[1][1];
for (i = 0; i < l - 1; i++) {
for (j = 0; j < l - 1; j++) {
if (i == 0 && j != 0) {
a[i][j] = a[i][j + 1];
}
else if (j == 0 && i != 0) {
a[i][j] = a[i + 1][j];
}
else if (i > 0 && j > 0) {
a[i][j] = a[i + 1][j + 1];
}
}
}//以上步骤用于消去矩阵的第2列和第2行
return sum(a, l - 1, s1);//对操作后的矩阵重复上述操作
}
}
int main() {
cin >> n;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
for (k = 0; k < n; k++) {
cin >> b[j][k];
}
}
cout << sum(b, n, 0) << endl;
}
return 0;
}