OpenJudge 1.12 04:最匹配的矩阵
【题目考点】 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)为左上角的子矩阵在任何情况下都考虑。
与解法2思路相似,不再赘述。
【题解代码】 解法1:枚举#include解法2:记忆化递归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; }
#include解法3:递推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; }
#includeusing 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; }



