#P4482. 第3题-带Padding的卷积计算
-
1000ms
Tried: 3
Accepted: 3
Difficulty: 5
所属公司 :
华为
时间 :2025年11月20日-AI方向
-
算法标签>机器学习算法
第3题-带Padding的卷积计算
解题思路
本题要求实现二维卷积 + 零填充(padding),并且题面明确说明是“无核翻转”的卷积,本质上就是二维相关运算(cross-correlation):
对每个输出像素 (i,j),让卷积核以 (i,j) 为中心覆盖在原图上,做逐元素乘加;超出边界的位置视为 0(即在图像外层补零)。
设:
- 卷积核尺寸:m×m,且 m 为奇数(方便居中)
- 图像尺寸:n×n
- 核心公式(按题意“无核翻转”):
其中:
- t=2m−1 为卷积核“半径”,即中心偏移
- S′ 为对原图 S 进行零填充后的概念扩展:索引越界时视为 0
在实现时有两种等价写法:
- 显式构造一个 (n+2t)×(n+2t) 的新数组,把原图放在中间,其余补 0,再做普通卷积。
- 不新建数组,在计算时判断坐标是否越界,越界就跳过(视为 0)。
为了节省空间,本题更适合使用方法 2: 对每个输出位置 (i,j):
-
初始化
sum = 0 -
枚举卷积核坐标
(u, v),范围0 .. m-1 -
将其映射到图像坐标:
x = i + u - ty = j + v - t
-
若
0 <= x < n且0 <= y < n,则sum += image[x][y] * kernel[u][v] -
最后
ans[i][j] = sum
按行输出结果即可。
代码实现
Python 实现
import sys
# 卷积函数,输入核 kernel 和图像 img,返回卷积结果
def convolve2d(kernel, img):
m = len(kernel) # 卷积核大小 m
n = len(img) # 图像大小 n
t = m // 2 # 核的半径 (padding 策略使用)
# 初始化结果矩阵
res = [[0] * n for _ in range(n)]
# 遍历每一个输出像素 (i, j)
for i in range(n):
for j in range(n):
s = 0
# 遍历卷积核 (u, v)
for u in range(m):
for v in range(m):
x = i + u - t # 映射到图像中的行坐标
y = j + v - t # 映射到图像中的列坐标
# 判断是否在原图范围内,越界视为 0
if 0 <= x < n and 0 <= y < n:
s += img[x][y] * kernel[u][v]
res[i][j] = s
return res
def main():
data = list(map(int, sys.stdin.read().split()))
# 读入 m, n
m, n = data[0], data[1]
idx = 2
# 读入卷积核 m 行,每行 m 个数
kernel = []
for _ in range(m):
row = data[idx:idx + m]
idx += m
kernel.append(row)
# 读入图像 n 行,每行 n 个数
img = []
for _ in range(n):
row = data[idx:idx + n]
idx += n
img.append(row)
# 进行卷积计算
res = convolve2d(kernel, img)
# 按题目要求输出,行内用空格分隔
out_lines = []
for i in range(n):
out_lines.append(" ".join(str(x) for x in res[i]))
sys.stdout.write("\n".join(out_lines))
if __name__ == "__main__":
main()
Java 实现
import java.util.Scanner;
public class Main {
// 卷积函数,输入核 kernel 和图像 img,返回卷积结果
public static int[][] convolve2d(int[][] kernel, int[][] img) {
int m = kernel.length; // 卷积核大小
int n = img.length; // 图像大小
int t = m / 2; // 核半径
int[][] res = new int[n][n]; // 结果矩阵
// 遍历输出矩阵中的每个位置
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int sum = 0;
// 遍历卷积核
for (int u = 0; u < m; u++) {
for (int v = 0; v < m; v++) {
int x = i + u - t; // 映射到图像行坐标
int y = j + v - t; // 映射到图像列坐标
// 如果在图像范围内则累加
if (x >= 0 && x < n && y >= 0 && y < n) {
sum += img[x][y] * kernel[u][v];
}
}
}
res[i][j] = sum;
}
}
return res;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读入 m, n
int m = sc.nextInt();
int n = sc.nextInt();
// 读入卷积核
int[][] kernel = new int[m][m];
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
kernel[i][j] = sc.nextInt();
}
}
// 读入图像
int[][] img = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
img[i][j] = sc.nextInt();
}
}
// 进行卷积
int[][] res = convolve2d(kernel, img);
// 输出结果
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (j > 0) sb.append(' ');
sb.append(res[i][j]);
}
if (i < n - 1) sb.append('\n');
}
System.out.print(sb.toString());
sc.close();
}
}
C++ 实现
#include <iostream>
#include <vector>
using namespace std;
// 卷积函数,输入核 kernel 和图像 img,返回卷积结果
vector<vector<long long>> convolve2d(const vector<vector<long long>>& kernel,
const vector<vector<long long>>& img) {
int m = (int)kernel.size(); // 卷积核大小
int n = (int)img.size(); // 图像大小
int t = m / 2; // 核半径
vector<vector<long long>> res(n, vector<long long>(n, 0));
// 遍历每一个输出位置 (i, j)
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
long long sum = 0;
// 遍历卷积核 (u, v)
for (int u = 0; u < m; ++u) {
for (int v = 0; v < m; ++v) {
int x = i + u - t; // 映射到图像行坐标
int y = j + v - t; // 映射到图像列坐标
// 判断是否在图像范围内部,越界视为 0
if (x >= 0 && x < n && y >= 0 && y < n) {
sum += img[x][y] * kernel[u][v];
}
}
}
res[i][j] = sum;
}
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int m, n;
// 读入 m, n
if (!(cin >> m >> n)) {
return 0;
}
// 读入卷积核
vector<vector<long long>> kernel(m, vector<long long>(m));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < m; ++j) {
cin >> kernel[i][j];
}
}
// 读入图像
vector<vector<long long>> img(n, vector<long long>(n));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin >> img[i][j];
}
}
// 进行卷积
vector<vector<long long>> res = convolve2d(kernel, img);
// 输出结果
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (j > 0) cout << ' ';
cout << res[i][j];
}
if (i + 1 < n) cout << '\n';
}
return 0;
}
题目内容
卷积计算是人工智应用的常见计算操作,在深度学习中,卷积一般使用无核翻转的方式,通过如下公式计算:
$(S \cdot K)(i, j)-\sum_{m} \sum_{n} S(i+m, j+n) \cdot K(m, n)$
其中:
S 是输入图像(或二维信号)
K 是卷积核(或滤波器)
(i,j) 是输出图像的坐标
(m,n) 是卷积核的坐标
所谓 Padding 是根据卷积的尺寸,在输入图像外围填充若干圈 0 ,确保卷积计算后的辅出数据尺寸与原始输入一致。
如:

输入描述
第一行 为输入卷积尺寸 m∗m 和图像尺寸 n∗n,m 为大于 1 的奇数,n 为大于 1 的整数
然后是卷积矩阵数据,共 m 行,每行为卷积核的某一行,值的取值范围 [−10,10] 整数
最后是图像数据,共 n 行,每行为图像数据的某一行,值的取值范围 [0,255] 整数

如:
3 3
1 0 -1
1 0 -1
1 0 -1
1 2 3
4 5 6
7 8 9
输出描述
输出为带 padding 卷积操作后结果数据,大小为输入图像的尺寸 n∗n ,如:
-7 -4 7
-15 -6 15
-13 -4 13
样例1
输入
3 5
-5 4 0
0 -3 -2
3 2 0
231 112 85 120 114
154 237 168 55 35
203 204 160 70 7
194 32 36 99 181
64 185 251 30 116
输出
-609 430 552 26 -107
394 -737 98 440 -25
-13 -108 -965 -538 603
294 195 371 -366 -545
214 -1899 -829 -104 -116
样例2
输入
3 5
10 -1
10 -1
10 -1
3 2 5 4 8
1 8 2 4 1
3 7 5 2 9
8 2 1 4 3
1 5 7 2 8
输出
-10 -3 2 -2 8
-17 -5 7 -6 10
-17 4 7 -5 10
-14 -1 6 -7 8
-7 1 1 -3 -6