1. 最后一列初始化错误(最致命)
UVA116 要求从第一列任意行出发,到达最后一列任意行结束。因此最后一列的 dp[i][m-1] 应该等于 a[i][m-1],表示从该位置到终点的最小和就是它本身。
你的代码:
cpp
for (int i=0;i<n-1;i++) dp[i][m-1]=INF;
dp[n-1][m-1]=a[n-1][m-1];
这相当于强制路径必须走到最后一行结束,其他行无法到达终点,所以整个 DP 都是错的。
修正:
cpp
for (int i = 0; i < n; i++) dp[i][m-1] = a[i][m-1];
2. 起点选择错误
你直接从 x=0 开始输出路径,并认为起点是第 0 行。但最优路径的起点不一定是第 0 行。应该遍历第一列,找出 dp[i][0] 最小的行;如果有多行最小值相同,选择行号最小的那个(因为题目要求字典序最小)。
修正:
cpp
int start = 0;
for (int i = 1; i < n; i++) {
if (dp[i][0] < dp[start][0]) start = i;
}
// 如果值相等,因为 i 从小到大遍历,自然保留行号小的
3. 路径查找时数组越界
你在查找下一步时:
cpp
if (dp[x+i][j+1]+a[x][j]==dp[x][j]) {
x=get_row(x+i);
break;
}
这里 x+i 可能为 -1 或 n,直接访问 dp[-1][j+1] 或 dp[n][j+1] 会越界。应该用 get_row(x+i) 计算实际行号。
修正:
cpp
int next_row = get_row(x+i);
if (dp[next_row][j+1] + a[x][j] == dp[x][j]) { ... }
4. 字典序最小未处理
题目要求如果有多条最小路径,输出行号序列字典序最小的。你的查找顺序是 i = -1, 0, 1,对应的行号是 (x-1+n)%n, x, (x+1)%n,这不是 按行号从小到大排列的。例如 n=5, x=0,候选行号为 4, 0, 1,实际大小顺序是 0, 1, 4。你应该先对三个候选行号排序,然后按从小到大检查。
修正:
cpp
int rows[3] = {get_row(x-1), x, get_row(x+1)};
sort(rows, rows+3);
for (int k = 0; k < 3; k++) {
int nr = rows[k];
if (dp[nr][j+1] + a[x][j] == dp[x][j]) {
x = nr;
break;
}
}
5. 最终输出错误
你的输出:
cpp
cout<<n<<'\n'<<dp[0][0]<<'\n';
这里 n 是行数,不是最后一列的行号;dp[0][0] 也不是最小和(因为起点可能不是第 0 行)。应该在循环输出完前 m-1 列后,输出最后一列的行号 x+1,然后输出 dp[start][0]。
修正:
cpp
// 循环输出前 m-1 列的行号
for (int j = 0; j < m-1; j++) {
cout << x+1 << ' ';
// ... 更新 x ...
}
cout << x+1 << '\n'; // 输出最后一列的行号
cout << dp[start][0] << '\n'; // 输出最小和