ACM解题总结——HihoCoder1363

题目来源:

    HihoCoder1363

题目要求:

    在图像处理的技术中,经常会用到算子与图像进行卷积运算,从而达到平滑图像或是查找边界的效果。
    假设原图为H×W的矩阵A,算子矩阵为D×D的矩阵Op,则处理后的矩阵B大小为(H-D+1)×(W-D+1)。其中:
                                                  B[i][j] = ∑(A[i-1+dx][j-1+dy]*Op[dx][dy]) | (dx = 1 .. D, dy = 1 .. D), 1 ≤ i ≤ H-D+1, 1 ≤ j ≤ W-D+1
给定矩阵AB,以及算子矩阵的边长D。你能求出算子矩阵中每个元素的值吗?

输入输出格式:

    输入:

1行:3个整数,H, W, D,分别表示原图的高度和宽度,以及算子矩阵的大小。5≤H,W≤601≤D≤5D一定是奇数。
2..H+1行:每行W个整数,第i+1行第j列表示A[i][j]0≤A[i][j]≤255
接下来H-D+1行:每行W-D+1个整数,表示B[i][j]B[i][j]在int范围内,可能为负数。
输入保证有唯一解,并且解矩阵的每个元素都是整数。

输出:

1..D行:每行D个整数,第i行第j列表示Op[i][j]

解答:

·概述:

    本题本质上是一个求解线性方程组的问题。根据题目中对于B[i][j]的计算方式的描述,当已知A[i][j]B[i][j]的时候,可以列出(H-D+1) × (W-D+1)个线性方程,而矩阵Op的大小为D×D,所以,可以组成一个包含D×D个未知数、(H-D+1) × (W-D+1)个方程的线性方程组。之后的步骤,可以依据线性代数的知识,通过矩阵运算求解本题答案。


·利用矩阵求解线性方程组:

    根据线性代数的知识,利用矩阵求解线性方程组的步骤为:根据方程组列出增广矩阵、通过矩阵运算将将增广矩阵转换为阶梯型,然后求出未知数的解。根据本题给出的参数,得到的增广矩阵的大小将会是:(H-D+1)×(W-D+1)行、D×D列。本题的难度在于代码实现矩阵求解的过程,这里简要编程思路和主要逻辑代码,完整的程序代码在本文末尾给出。

·矩阵的定义和基本操作:

    对于矩阵,利用二维数据可以很容易地在程序中定义,如下:      
    matrix = new double[(H - D + 1) * (W - D + 1)][D * D];
    private static double[] result;
    result = new double[(H - D + 1) * (W - D + 1)];
在求解线性方程组中,涉及到的矩阵基本操作有:交换两行,一行乘一个系数后加到另一行,这里定义两个方法来对应这两种基本操作,如下:
 private static void changeRow(int r1, int r2) {        
        for (int i = 0; i < D * D; i++) {
            double tmp = matrix[r1][i];
            matrix[r1][i] = matrix[r2][i];
            matrix[r2][i] = tmp;
        }
        double tmp = result[r1];
        result[r1] = result[r2];
        result[r2] = tmp;
    }

    private static void eliminate(int r1, int r2, double multiply) {
        for (int i = 0; i < D * D; i++) {
            matrix[r1][i] = matrix[r1][i] + matrix[r2][i] * multiply;
        }
        result[r1] = result[r1] + result[r2] * multiply;
    }

·阶梯化处理:

     有了以上的数据结构后,可以对矩阵进行阶梯化处理了,处理过程代码描述如下:
   private static void diagonalization() {
        for (int i = 0; i < D * D; i++) {
            if (checkZero(matrix[i][i])) {
                for (int j = i + 1; j < (H - D + 1) * (W - D + 1); j++) {
                    if (!checkZero(matrix[j][i])) {
                        changeRow(i, j);
                        break;
                    }
                }
            }
            
            for(int j = i + 1; j < (H - D + 1) * (W - D + 1); j++) {
                if(!checkZero(matrix[j][i])) {
                    eliminate(j, i, -1.0 * matrix[j][i] / matrix[i][i]);
                }
            }
        }
    }
     以上为本题的思路,需要注意的是,虽然题目说明结果一定是整数,但计算过程中可能会出现小数,因此,在矩阵运算过程中,矩阵的存储应使用 double类型,在输出最后结果时,有double转为int的过程中,为了保证结果的正确,需采用四舍五入的方式进行处理,Math类中的round方法可以实现double类型到long类型的四舍五入方式的转化。

程序代码:

package hihocoder;

import java.io.PrintWriter;
import java.util.Scanner;

/**
 * This is the ACM problem solving program for hihoCoder 1363.
 * 
 * @version 2018-03-18
 * @author Zhang Yufei.
 */
public class Main{
    /** For input. */
    private static Scanner scan;

    /** For output. */
    private static PrintWriter writer;

    /** Input data */
    private static int H, W, D;

    /** Input data */
    private static int[][] A, B;

    /** The matrix used for Gauss elimination. */
    private static double[][] matrix;

    /** The result vector for Gauss elimination. */
    private static double[] result;

    /** Record the result */
    private static int[] op;

    /**
     * The main program.
     * 
     * @param args
     *            The command-line parameters list.
     */
    public static void main(String[] args) {
        initIO();

        input();
        diagonalization();
        compute();

        closeIO();
    }

    /**
     * Deal with the input data.
     */
    private static void input() {
        H = scan.nextInt();
        W = scan.nextInt();
        D = scan.nextInt();

        A = new int[H][W];
        B = new int[H - D + 1][W - D + 1];
        op = new int[D * D];
        matrix = new double[(H - D + 1) * (W - D + 1)][D * D];
        result = new double[(H - D + 1) * (W - D + 1)];

        for (int i = 0; i < H; i++) {
            for (int j = 0; j < W; j++) {
                A[i][j] = scan.nextInt();
            }
        }

        for (int i = 0; i < H - D + 1; i++) {
            for (int j = 0; j < W - D + 1; j++) {
                B[i][j] = scan.nextInt();
            }
        }

        for (int i = 0; i < H - D + 1; i++) {
            for (int j = 0; j < W - D + 1; j++) {
                makeRow(i, j);
            }
        }
        
//        for (int i = 0; i < (H - D + 1) * (W - D + 1); i++) {
//            for (int j = 0; j < D * D; j++) {
//                System.out.printf("%+03.2f ", matrix[i][j]);
//            }
//            System.out.printf("%+03.2f\n", result[i]);
//        }
//        System.out.println();
    }

    /**
     * Make one row in the matrix.
     * 
     * @param r
     *            The row of result.
     * @param c
     *            The column of result.
     */
    private static void makeRow(int r, int c) {
        int row = r * (W - D + 1) + c;
        for (int i = 0; i < D; i++) {
            for (int j = 0; j < D; j++) {
                matrix[row][i * D + j] = A[r + i][c + j];
            }
        }

        result[row] = B[r][c];
    }

    /**
     * Compute the result, getting the answer of this problem.
     */
    private static void compute() {
        for(int i = D * D - 1; i >= 0; i--) {
            if(checkZero( matrix[i][i])) {
                continue;
            }
            for(int j = D * D - 1; j > i; j--) {
                result[i] -= matrix[i][j] * op[j];
            }
            op[i] = (int) Math.round(result[i] / matrix[i][i]);
        }
        
        for(int i = 0; i < D * D; i++) {
            System.out.print(op[i] + " ");

            if(i % D == D - 1) {
                System.out.println();
            }
            
        }
    }
    /**
     * Convert the matrix into diagonalization, which means that the value of
     * any element in position (i, j) is zero if the i is greater than j.
     */
    private static void diagonalization() {
        for (int i = 0; i < D * D; i++) {
            if (checkZero(matrix[i][i])) {
                for (int j = i + 1; j < (H - D + 1) * (W - D + 1); j++) {
                    if (!checkZero(matrix[j][i])) {
                        changeRow(i, j);
                        break;
                    }
                }
            }
            
            for(int j = i + 1; j < (H - D + 1) * (W - D + 1); j++) {
                if(!checkZero(matrix[j][i])) {
                    eliminate(j, i, -1.0 * matrix[j][i] / matrix[i][i]);
                }
            }
        }
    }

    /**
     * Check if the given double num is zero.
     * 
     * @param num
     *            The number to check.
     * @return Returns if the number is zero.
     */
    private static boolean checkZero(double num) {
        if (Math.abs(num) <= 1e-9) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Change two rows in matrix.
     * 
     * @param r1
     *            one row.
     * @param r2
     *            another row.
     */
    private static void changeRow(int r1, int r2) {
        for (int i = 0; i < D * D; i++) {
            double tmp = matrix[r1][i];
            matrix[r1][i] = matrix[r2][i];
            matrix[r2][i] = tmp;
        }
        
        double tmp = result[r1];
        result[r1] = result[r2];
        result[r2] = tmp;
    }

    /**
     * Eliminate one element in matrix, changing it into zero by computing r1 =
     * r1 + multiply * r2;
     * 
     * @param r1
     *            one row.
     * @param r2
     *            another row.
     * @param multiply
     *            Multiply parameter.
     */
    private static void eliminate(int r1, int r2, double multiply) {
        for (int i = 0; i < D * D; i++) {
            matrix[r1][i] = matrix[r1][i] + matrix[r2][i] * multiply;
        }
        result[r1] = result[r1] + result[r2] * multiply;
    }

    /**
     * Initial the input and output.
     */
    private static void initIO() {
        try {
            scan = new Scanner(System.in);
            writer = new PrintWriter(System.out);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Close the input and output.
     */
    private static void closeIO() {
        scan.close();
        writer.close();
    }
}

你可能感兴趣的:(ACM)