【题目链接】
【题目考点】
1. 枚举
2. 递推/递归
【解题思路】
解法1:枚举
行坐标i从1遍历到m-r+1,列坐标j从1遍历到n-s+1,取以(i, j)为左上角的子矩阵C,看该矩阵与B矩阵对应元素差值的绝对值之和为多少。求这个加和最小时(i,j)的值,输出矩阵A中以(i,j)为左上角的r行s列的子矩阵。
解法2:记忆化递归
设Node类型,包含属性x与y,以及加和sum,表示A矩阵中左上角为(x,y)的r行s列子矩阵与B矩阵对应元素差值绝对值加和为sum。
- 状态定义:
f[i][j]
为Node类型元素,表示行下标范围1~i,列下标范围1~j中的与B矩阵对应元素差值绝对值之和最小的子矩阵C。 - 递归问题:记为
solve(i, j)
:求行下标范围1~i,列下标范围1~j中与B矩阵对应元素差值绝对值之和最小的子矩阵。 - 递归关系:要求
solve(i, j)
,可以先求solve(i-1, j)
与solve(i, j-1)
,得到两个子矩阵。还有一个左上角为(i-r+1,j-s+1)的,以(i,j)为右下角的子矩阵,比较这三个子矩阵与B矩阵的对应元素差值的绝对值加和,取其中加和最小的矩阵作为solve(i,j)
的结果。 - 递归出口:
- 当i为r时,只考虑
solve(i, j-1)
,不考虑solve(i-1, j)
。 - 当j为s时,只考虑
solve(i-1, j)
,不考虑solve(i, j-1)
。
以(i-r+1,j-s+1)
为左上角的子矩阵在任何情况下都考虑。
- 当i为r时,只考虑
解法3:递推
与解法2思路相似,不再赘述。
【题解代码】
解法1:枚举
#include <bits/stdc++.h>
using namespace std;
#define N 105
#define INF 0x3f3f3f3f
int m, n, r, s, ri, rj, minSum = INF;//ri, rj保存结果
int a[N][N], b[N][N];
int sumMatrix(int si, int sj)//a[si][sj]~a[si+r-1][sj+s-1]这个子矩阵与b对应元素差的绝对值加和
{
int sum = 0;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= s; ++j)
sum += abs(a[si+i-1][sj+j-1] - b[i][j]);
return sum;
}
int main()
{
cin >> m >> n;
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
cin >> a[i][j];
cin >> r >> s;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= s; ++j)
cin >> b[i][j];
for(int i = 1; i <= m-r+1; ++i)
for(int j = 1; j <= n-s+1; ++j)
{
int sum = sumMatrix(i, j);//矩阵C与B对应元素差值绝对值之和
if(sum < minSum)//取最小值
{
minSum = sum;
ri = i, rj = j;//记录矩阵左上角
}
}
for(int i = ri; i <= ri+r-1; ++i)
{
for(int j = rj; j <= rj+s-1; ++j)
cout << a[i][j] << ' ';
cout << endl;
}
return 0;
}
解法2:记忆化递归
#include <bits/stdc++.h>
using namespace std;
#define N 105
#define INF 0x3f3f3f3f
int m, n, r, s;
int a[N][N], b[N][N];
struct Node
{
int x, y, sum;//矩阵A中以(x,y)为左上角r行s列的子矩阵与B对应元素差值绝对值加和为sum。
Node()
{
sum = INF;//sum初值设为无穷大
}
Node(int _x, int _y)//设置左上角位置,本函数中求出s
{
x = _x, y = _y, sum = 0;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= s; ++j)
sum += abs(a[x+i-1][y+j-1] - b[i][j]);
}
};
Node f[N][N];//行下标范围1~i,列下标范围1~j中的与B矩阵对应元素差值绝对值之和最小的子矩阵C。
//求三个Node元素中s最小的Node
Node minNode(Node a, Node b, Node c)
{
if(a.sum <= b.sum && a.sum <= c.sum)
return a;
else if(b.sum <= a.sum && b.sum <= c.sum)
return b;
else
return c;
}
Node solve(int i, int j)
{
if(f[i][j].x > 0 || f[i][j].y > 0)//状态有值
return f[i][j];
Node n1, n2, n3;//如果不赋值,属性s默认为无穷大
n3 = Node(i-r+1, j-s+1);
if(i > r)
n1 = solve(i-1, j);
if(j > s)
n2 = solve(i, j-1);
return f[i][j] = minNode(n1, n2, n3);//求三者中s最小的那一项
}
int main()
{
cin >> m >> n;
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
cin >> a[i][j];
cin >> r >> s;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= s; ++j)
cin >> b[i][j];
Node nr = solve(m, n);
for(int i = nr.x; i <= nr.x+r-1; ++i)
{
for(int j = nr.y; j <= nr.y+s-1; ++j)
cout << a[i][j] << ' ';
cout << endl;
}
return 0;
}
解法3:递推
#include <bits/stdc++.h>
using namespace std;
#define N 105
#define INF 0x3f3f3f3f
int m, n, r, s;
int a[N][N], b[N][N];
struct Node
{
int x, y, sum;//矩阵A中以(x,y)为左上角r行s列的子矩阵与B对应元素差值绝对值加和为sum。
Node()
{
sum = INF;//sum初值设为无穷大
}
Node(int _x, int _y)//设置左上角位置,本函数中求出s
{
x = _x, y = _y, sum = 0;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= s; ++j)
sum += abs(a[x+i-1][y+j-1] - b[i][j]);
}
};
Node f[N][N];//行下标范围1~i,列下标范围1~j中的与B矩阵对应元素差值绝对值之和最小的子矩阵C。
//求三个Node元素中s最小的Node
Node minNode(Node a, Node b, Node c)
{
if(a.sum <= b.sum && a.sum <= c.sum)
return a;
else if(b.sum <= a.sum && b.sum <= c.sum)
return b;
else
return c;
}
int main()
{
cin >> m >> n;
for(int i = 1; i <= m; ++i)
for(int j = 1; j <= n; ++j)
cin >> a[i][j];
cin >> r >> s;
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= s; ++j)
cin >> b[i][j];
f[r][s] = Node(1, 1);//行下标范围1~r,列下标范围1~s中,只有一个r行s列的子矩阵,左上角为(1,1)
for(int i = r; i <= m; ++i)
for(int j = s; j <= n; ++j)
{
Node n1, n2, n3;//如果不赋值,属性s默认为无穷大
n3 = Node(i-r+1, j-s+1);
if(i > r)
n1 = f[i-1][j];
if(j > s)
n2 = f[i][j-1];
f[i][j] = minNode(n1, n2, n3);
}
for(int i = f[m][n].x; i <= f[m][n].x+r-1; ++i)
{
for(int j = f[m][n].y; j <= f[m][n].y+s-1; ++j)
cout << a[i][j] << ' ';
cout << endl;
}
return 0;
}