华为非AI方向笔试真题【最小颜色修改代价】

最小颜色修改代价(C++/Py/Java/Js/Go)题解

华为笔试真题 6月17号 非AI方向第三题 300分题型

题目内容

有一个N×MN \times MN×M的网格,每个格子存一个颜色值(111 到 CCC 正整数)。小明从左上角 (0,0)(0,0)(0,0)出发,只能向右或向下移动到右下角 (N−1,M−1)(N-1,M-1)(N−1,M−1)。

如果路径上格子的颜色不同,需要支付代价来改变格子的颜色,使该路径上的格子颜色值相同。改变一个格子的颜色代价为 新颜色值-原颜色值 (相减的绝对值)

求从 (0,0)(0,0)(0,0) 到 (N−1,M−1)(N-1,M-1)(N−1,M−1) 的最小总修改代价。(行和列均从 000 开始)

输入描述

第一行:三个整数N,M,CN,M,CN,M,C

NNN:矩阵行数(1≤N≤501 \le N \le 501≤N≤50)

MMM:矩阵列数(1≤M≤501 \le M \le 501≤M≤50)

CCC:可选颜色数(1≤C≤501 \le C \le 501≤C≤50)

接下来 NNN 行,每行 MMM 个整数,表示矩阵每个格子的颜色值 aija_{ij}aij(1≤aij≤C1 \le a_{ij} \le C1≤aij≤C)

输出描述

输出一个整数,表示最小总修改代价

样例1

输入

复制代码
3 3 3
2 3 3
1 2 3
2 3 1

输出

复制代码
3

说明

如路径 (0,0)→(0,1)→(0,2)→(1,2)→(2,2)(0,0) \to (0,1) \to (0,2) \to (1,2) \to (2,2)(0,0)→(0,1)→(0,2)→(1,2)→(2,2),可将格子的颜色都修改为 333,修改代价如下:

(0,0)(0,0)(0,0) 从 222 改为 333,代价为 ∣3−2∣=1|3-2|=1∣3−2∣=1

(0,1)(0,1)(0,1) 颜色值为 333,无需修改,代价为 000

(0,2)(0,2)(0,2) 颜色值为 333,无需修改,代价为 000

(1,2)(1,2)(1,2) 颜色值为 333,无需修改,代价为 000

(2,2)(2,2)(2,2) 从 111 改为 333,代价为 ∣3−1∣=2|3-1|=2∣3−1∣=2

该路径颜色都改为 333 的总修改代价为 1+0+0+0+2=31+0+0+0+2=31+0+0+0+2=3

同理,该路径将格子颜色修改为 111 和 222 的总修改代价分别为 777 和 444,因此该路径的最小总修改代价为 333

同理,得到其他路径的最小总修改代价,取最小总修改代价最小的路径的总修改代价,结果为 333

样例2

输入

复制代码
2 3 20
1 2 20
20 20 20

输出

复制代码
19

说明

如路径 (0,0)→(0,1)→(0,2)→(1,2)(0,0) \to (0,1) \to (0,2) \to (1,2)(0,0)→(0,1)→(0,2)→(1,2),可将格子的颜色都修改为 202020,修改代价如下:

(0,0)(0,0)(0,0) 从 111 改为 333,代价为 ∣3−1∣=2|3-1|=2∣3−1∣=2

(0,1)(0,1)(0,1) 从 222 改为 333,代价为 ∣3−2∣=1|3-2|=1∣3−2∣=1

(0,2)(0,2)(0,2) 从 202020 改为 333,代价为 ∣3−20∣=17|3-20|=17∣3−20∣=17

(1,2)(1,2)(1,2) 从202020 改为 333,代价为 ∣3−20∣=17|3-20|=17∣3−20∣=17

该路径颜色都改为 333 的总修改代价为 2+1+17+17=372+1+17+17=372+1+17+17=37

同理,可以得到:该路径上,将格子颜色都改为 2,202,202,20 之间的任一整数,总修改代价最小,均为 373737,因此该路径的最小总修改代价为 373737

同理,观察其他路径的最小总修改代价:

路径 (0,0)→(0,1)→(1,1)→(1,2)(0,0) \to (0,1) \to (1,1) \to (1,2)(0,0)→(0,1)→(1,1)→(1,2):颜色值都为 2,202,202,20 之间的任一整数,总修改代价为 373737

路径 (0,0)→(1,0)→(1,1)→(1,2)(0,0) \to (1,0) \to (1,1) \to (1,2)(0,0)→(1,0)→(1,1)→(1,2):颜色值都为 202020,总修改代价为 191919

因此,总修改代价最小的路径为 (0,0)→(1,0)→(1,1)→(1,2)(0,0) \to (1,0) \to (1,1) \to (1,2)(0,0)→(1,0)→(1,1)→(1,2),且将格子颜色都改为 202020,结果为 191919

题解

思路

动态规划算法

  1. 由于1≤N≤501 \le N \le 501≤N≤50 、1≤M≤501 \le M \le 501≤M≤50、1≤C≤501 \le C \le 501≤C≤50 数据量,可以枚举路径颜色。然后使用动态规划计算从左上角到右下角的最小成本值。结果就为所有颜色到达右小角的最小值。

  2. 每个颜色计算成本的逻辑,定义dp[i][j]表示到达{i,j}的最小成本。初始dp[0][0] = abs(grid[i][j] - color(枚举的颜色))

  3. 状态转移方程

    • dp[i][j] = min(dp[i][j], dp[i-1][j] + abs(grid[i][j] - color)) , 当i > 0
    • dp[i][j] = min(dp[i][j], dp[i][j-1] + abs(grid[i][j] - color)) , 当j > 0
    1. 总体代码时间复杂为O(nmc)

C++

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n,m,c;
    cin >> n >> m >> c;
    vector<vector<int>> grid(n, vector<int>(m));
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> grid[i][j];
        }
    }
    
    int ans = INT_MAX;
    
    // 枚举路径统一颜色
    for (int color = 1; color <= c; color++) {
        vector<vector<int>> dp(n, vector<int>(m, INT_MAX));
        dp[0][0] = abs(grid[0][0] - color);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (i == 0 && j == 0) {
                    continue;
                }
                int cost = abs(grid[i][j] - color);
                if (i > 0) {
                    dp[i][j] = min(dp[i][j], dp[i-1][j] + cost);
                }
                if (j > 0) {
                    dp[i][j] = min(dp[i][j], dp[i][j-1] + cost);
                }
            }
        }
        ans = min(ans, dp[n-1][m-1]);
    }
    cout << ans;
    return 0;
}

java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());
        int c = Integer.parseInt(st.nextToken());

        int[][] grid = new int[n][m];

        for (int i = 0; i < n; i++) {
            st = new StringTokenizer(br.readLine());
            for (int j = 0; j < m; j++) {
                grid[i][j] = Integer.parseInt(st.nextToken());
            }
        }

        int ans = Integer.MAX_VALUE;

        // 枚举路径统一颜色
        for (int color = 1; color <= c; color++) {
            int[][] dp = new int[n][m];

            for (int i = 0; i < n; i++) {
                Arrays.fill(dp[i], Integer.MAX_VALUE);
            }

            dp[0][0] = Math.abs(grid[0][0] - color);

            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    if (i == 0 && j == 0) {
                        continue;
                    }

                    int cost = Math.abs(grid[i][j] - color);

                    if (i > 0) {
                        dp[i][j] = Math.min(dp[i][j], dp[i - 1][j] + cost);
                    }

                    if (j > 0) {
                        dp[i][j] = Math.min(dp[i][j], dp[i][j - 1] + cost);
                    }
                }
            }

            ans = Math.min(ans, dp[n - 1][m - 1]);
        }

        System.out.print(ans);
    }
}

python

python 复制代码
import sys

n, m, c = map(int, sys.stdin.readline().split())

grid = []
for _ in range(n):
    grid.append(list(map(int, sys.stdin.readline().split())))

ans = float('inf')

# 枚举路径统一颜色
for color in range(1, c + 1):
    dp = [[float('inf')] * m for _ in range(n)]

    dp[0][0] = abs(grid[0][0] - color)

    for i in range(n):
        for j in range(m):
            if i == 0 and j == 0:
                continue

            cost = abs(grid[i][j] - color)

            if i > 0:
                dp[i][j] = min(dp[i][j], dp[i - 1][j] + cost)

            if j > 0:
                dp[i][j] = min(dp[i][j], dp[i][j - 1] + cost)

    ans = min(ans, dp[n - 1][m - 1])

print(ans)

javascript

js 复制代码
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const input = [];

rl.on('line', (line) => {
    input.push(line);
});

rl.on('close', () => {
    let idx = 0;

    let [n, m, c] = input[idx++].split(' ').map(Number);

    const grid = [];

    for (let i = 0; i < n; i++) {
        grid.push(input[idx++].split(' ').map(Number));
    }

    let ans = Number.MAX_SAFE_INTEGER;

    // 枚举路径统一颜色
    for (let color = 1; color <= c; color++) {
        const dp = Array.from(
            { length: n },
            () => Array(m).fill(Number.MAX_SAFE_INTEGER)
        );

        dp[0][0] = Math.abs(grid[0][0] - color);

        for (let i = 0; i < n; i++) {
            for (let j = 0; j < m; j++) {
                if (i === 0 && j === 0) {
                    continue;
                }

                const cost = Math.abs(grid[i][j] - color);

                if (i > 0) {
                    dp[i][j] = Math.min(
                        dp[i][j],
                        dp[i - 1][j] + cost
                    );
                }

                if (j > 0) {
                    dp[i][j] = Math.min(
                        dp[i][j],
                        dp[i][j - 1] + cost
                    );
                }
            }
        }

        ans = Math.min(ans, dp[n - 1][m - 1]);
    }

    console.log(ans);
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
)

func main() {
	in := bufio.NewReader(os.Stdin)

	var n, m, c int
	fmt.Fscan(in, &n, &m, &c)

	grid := make([][]int, n)
	for i := 0; i < n; i++ {
		grid[i] = make([]int, m)
		for j := 0; j < m; j++ {
			fmt.Fscan(in, &grid[i][j])
		}
	}

	ans := math.MaxInt32

	// 枚举路径统一颜色
	for color := 1; color <= c; color++ {
		dp := make([][]int, n)

		for i := 0; i < n; i++ {
			dp[i] = make([]int, m)
			for j := 0; j < m; j++ {
				dp[i][j] = math.MaxInt32
			}
		}

		if grid[0][0] > color {
			dp[0][0] = grid[0][0] - color
		} else {
			dp[0][0] = color - grid[0][0]
		}

		for i := 0; i < n; i++ {
			for j := 0; j < m; j++ {
				if i == 0 && j == 0 {
					continue
				}

				cost := grid[i][j] - color
				if cost < 0 {
					cost = -cost
				}

				if i > 0 {
					if dp[i-1][j]+cost < dp[i][j] {
						dp[i][j] = dp[i-1][j] + cost
					}
				}

				if j > 0 {
					if dp[i][j-1]+cost < dp[i][j] {
						dp[i][j] = dp[i][j-1] + cost
					}
				}
			}
		}

		if dp[n-1][m-1] < ans {
			ans = dp[n-1][m-1]
		}
	}

	fmt.Print(ans)
}