日期:2025年5月18日
ZeroJudge 題目連結:k931. P8. 切割蜂蜜 (Honey)
解題想法
這題有 3 個重點:
- 要設計儲存六角形蜂巢資料的方式,我是用二維串列,偶數列的欄位為 1, 3, 5, ...,奇數列的欄位為 0, 2, 4, ...,這個寫法的優點是讀取資料時索引值比較好寫,缺點是會浪費接近一半的二維串列空間。
- 用動態規畫計算從左到右切割到每一格時最小的累積損失量,取這一格的左上、左側、左下之中的最小值,再加上這格原來的值。
- 為了避免檢查邊界值要寫一堆 if,我選擇在四周 (C++) 或是最右側、最下方 (Python) 加上正無窮大的值當作哨兵,缺點是要多花一點記憶體。
Python 程式碼
使用時間約為 1.1 s,記憶體約為 26.7 MB,通過測試。
import sys
oo = float('inf')
for line in sys.stdin:
H, W = map(int, line.split()) # 晶格的高與寬
grid = [[-1]*(2*W+2) for _ in range(H)] # 晶格 (H+1)*(2*W+2)
grid.append([oo]*(2*W+2)) # 最下方補一列 oo
for r in range(H): # 讀取 H 行資料
row = list(map(int, input().split()))
if r%2 == 0: # 如果是偶數列,欄位為 1, 3, 5, ..., 2*W - 1
for i, v in enumerate(row): # 索引值 i,數值 v
grid[r][2*i+1] = v
grid[r][2*W] = grid[r][2*W+1] = oo # 最後面補上 oo 當作哨兵
else: # 如果是奇數列,欄位為 0, 2, 4, ..., 2*W
for i, v in enumerate(row): # 索引值 i,數值 v
grid[r][2*i] = v
grid[r][2*W+1] = oo # 最後面補上 oo 當作哨兵
# 動態規畫,grid[r][c] 改成到這格損失的值,加上左上、左側、左下最小值
for c in range(2, 2*W+1): # 依序掃過第 2 ~ 2*W 欄
if c%2 == 0: # 如果是偶數欄,列為 1, 3, 5, ...
for r in range(1, H, 2):
grid[r][c] += min(grid[r-1][c-1], grid[r][c-2], grid[r+1][c-1])
else: # 如果是奇數欄,列為 0, 2, 4, ...
for r in range(0, H, 2):
grid[r][c] += min(grid[r-1][c-1], grid[r][c-2], grid[r+1][c-1])
# 找出答案,在每一列的最後一格,不含 oo
ans = oo # 答案預設為最大值
for r in range(H): # 依序掃過每一列
if r%2 == 0: ans = min(ans, grid[r][2*W-1]) # 如果是偶數列
else: ans = min(ans, grid[r][2*W]) # 如果是奇數列
print(ans)
C++ 程式碼
使用時間約為 71 ms,記憶體約為 3.9 MB,通過測試。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int oo = 1E9 + 1;
int main() {
int H, W; // 晶格的高與寬
while(scanf("%d %d", &H, &W) != EOF) {
int grid[H+2][2*W+2]; // 晶格 (H+2)*(2*W+2)
memset(grid, -1, sizeof(grid)); // 先填入 -1
for(int c=0; c<2*W+2; c++) {
grid[0][c] = oo; // 最上方為一列 oo
grid[H+1][c] = oo; // 最下方為一列 oo
}
for(int r=1; r<=H; r++) { // 讀取 H 行資料
if (r%2 == 1) { // 如果是奇數列,欄位為 1, 3, 5, ..., 2*W - 1
for(int i=0; i<W; i++) { // 索引值 i
scanf("%d", &grid[r][2*i+1]);
}
grid[r][2*W] = grid[r][2*W+1] = oo; // 最後面補上 oo 當作哨兵
} else { // 如果是偶數列,欄位為 0, 2, 4, ..., 2*W
for(int i=0; i<=W; i++) { // 索引值 i
scanf("%d", &grid[r][2*i]);
}
grid[r][2*W+1] = oo; // 最後面補上 oo 當作哨兵
}
}
// 動態規畫,grid[r][c] 改成到這格損失的值,加上左上、左側、左下最小值
for(int c=2; c<2*W+1; c++) { // 依序掃過第 2 ~ 2*W 欄
if (c%2 == 0) { // 如果是偶數欄,列為 2, 4, 6, ...
for(int r=2; r<=H; r+=2) {
grid[r][c] += min(grid[r-1][c-1], min(grid[r][c-2], grid[r+1][c-1]));
}
} else { // 如果是奇數欄,列為 1, 3, 4, ...
for(int r=1; r<=H; r+=2) {
grid[r][c] += min(grid[r-1][c-1], min(grid[r][c-2], grid[r+1][c-1]));
}
}
}
// 找出答案,在每一列的最後一格,不含 oo
int ans = oo; // 答案預設為最大值
for(int r=1; r<=H; r++) { // 依序掃過每一列
if (r%2 == 0) ans = min(ans, grid[r][2*W]); // 如果是偶數列
else ans = min(ans, grid[r][2*W-1]); // 如果是奇數列
}
printf("%d\n", ans);
}
return 0;
}
沒有留言:
張貼留言