最小颜色修改代价(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≤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 数据量,可以枚举路径颜色。然后使用动态规划计算从左上角到右下角的最小成本值。结果就为所有颜色到达右小角的最小值。
-
每个颜色计算成本的逻辑,定义
dp[i][j]表示到达{i,j}的最小成本。初始dp[0][0] = abs(grid[i][j] - color(枚举的颜色))。 -
状态转移方程
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时
- 总体代码时间复杂为
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)
}