OpenJudge NOI 1.12 04:最匹配的矩阵

【题目链接】

OpenJudge NOI 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)为左上角的子矩阵在任何情况下都考虑。
解法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;
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值