美图2017A卷第一题

时间限制:C/C++语言 1000MS;其他语言 3000MS
内存限制:C/C++语言 65536KB;其他语言 589824KB

题目描述:

若[i,j]和[i’,j’]是矩阵的两个位置, 他们的距离定义为max(|i-i’|, |j-j’|)。已知:
1. 整数 n > r >= 0;
2. F为 n * n 的矩阵, 矩阵内元素属于集合{0, 1};
3. 矩阵第i列第j行的元素用F[i,j]表示.

请编写程序,输出一个 n * n 的W矩阵。要求W[i,j] (矩阵W第i列第j行的元素)等于所有可能的F[x,y]的和, 其中[x,y]取和[i,j]的距离不大于r的所有位置。

输入

输入的第一行包含两个正整数n和r, 用一个空格隔开: 0 <= r < n <= 250。接下来会有n行表示矩阵F的元素, 每一行包含n个整数∈{0,1},用一个空格隔开。

输出

将来结果打印到标准输入输出。输出为n行,在第j行, 应该输出W[1,j]~W[n,j];每个元素中间用一个空格隔开。

样例输入

5 1
1 0 0 0 1
1 1 1 0 0
1 0 0 0 0
0 0 0 1 1
0 1 0 0 0

样例输出

3 4 2 2 1
4 5 2 2 1
3 4 3 3 2
2 2 2 2 2
1 1 2 2 2

暴力求解

#include <iostream>
#include <vector>

using namespace std;

int test(){

    int n, r;
    cin >> n >> r;

    vector<vector<int>> F(n, vector<int>(n, 0));
    vector<vector<int>> W(n, vector<int>(n, 0));

    int temp;
    for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
        cin >> F[i][j];

    // 对于每个W[i,j]
    for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
    {
        int startX = i - r >= 0 ? i - r : 0;
        int startY = j - r >= 0 ? j - r : 0;

        int endX = i + r < n ? i + r : n - 1;
        int endY = j + r < n ? j + r : n - 1;

        for (int m = startX; m <= endX; m++)
        for (int n = startY; n <= endY; n++)
            W[i][j] += F[m][n];
    }

    for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
        cout << W[i][j] << (j == n - 1 ? "\n" : " ");

    return 0;
}

上述方法因为在内层求和数涉及重复计算,所以时间复杂度略高(其复杂度为 O(n2×r2) ),可以参考其他思路(积分图)进行实现,如何减少重复计算呢?

积分矩阵可以事先求出(画一下图就知道很容易求得),其复杂度为 O(n2) ,然后利用S矩阵就可以快速求出要求的区域的和,这一步复杂度仍为 O(n2) ,最终整体复杂度为 O(n2)

源码

#include <iostream>
#include <vector>

using namespace std;

int test(){

    int n, r;
    cin >> n >> r;

    vector<vector<int>> F(n, vector<int>(n, 0));
    vector<vector<int>> S(n, vector<int>(n, 0));
    vector<vector<int>> W(n, vector<int>(n, 0));

    int temp;
    for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
        cin >> F[i][j];

    // 求S矩阵
    // i=0时
    S[0][0] = F[0][0];
    for (int i = 1; i < n; i++){
        S[i][0] = S[i - 1][0] + F[i][0];
    }
    // i=0时
    for (int j = 1; j < n; j++){
        S[0][j] = S[0][j - 1] + F[0][j];
    }
    // i,j>0时
    for (int i = 1; i < n; i++)
    for (int j = 1; j < n; j++)
        S[i][j] = S[i][j - 1] + S[i - 1][j] - S[i - 1][j - 1] + F[i][j];

    // 对于每个W[i,j]
    for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
    {
        // 注意越界,边界条件
        int startX = i - r >= 0 ? i - r : 0;
        int startY = j - r >= 0 ? j - r : 0;

        int endX = i + r < n ? i + r : n - 1;
        int endY = j + r < n ? j + r : n - 1;

        W[i][j] = S[endX][endY] 
            - (startY - 1 < 0 ? 0 : S[endX][startY - 1])
            - (startX - 1 < 0 ? 0 : S[startX - 1][endY])
            + (startX - 1 < 0 || startY - 1 < 0 ? 0 : S[startX - 1][startY - 1]);
    }

    for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
        cout << W[i][j] << (j == n - 1 ? "\n" : " ");

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值