题意:
找到一条纵向的线,使得线经过的格子值和最小,如果值相同取最右边的。(从每个格子可以通向八个方向)输出每个格子到列号(每行1个格子)
分析:
从上往下遍历,当前位置可能由上一行的三个方向过来
dp[i][j]=dp[i−1][k]+a[i][j](k=j−1,j,j+1)
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
k
]
+
a
[
i
]
[
j
]
(
k
=
j
−
1
,
j
,
j
+
1
)
因为这里用到j-1和j+1所以需要我们对第0列和第n+1列进行初始化,为了使答案中到列数不会出现0或n+1,初始化两列为无穷大的一个数,注意在这之前如果将dp数组定义在main函数里面需要每次对dp进行初始化为0,如果不这样的话,每次第一行和m+1行到内容不确定,会导致答案错误。
关于还原路径我们每次判断dp[i][j]的最优值顺便记录下它从上一行到那一列过来到就行,最后回溯回去就行。
code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
int path[110][110];
int ans[110];
int main(){
int T,cas = 0;
scanf("%d",&T);
while(T--){
int m,n;
scanf("%d%d",&m,&n);
int dp[110][110];
memset(dp,0,sizeof(dp));
for(int i = 0; i <= m+1; i++){
dp[i][0] = dp[i][n+1] = INF;
}
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
scanf("%d",&dp[i][j]);
}
}
for(int i = 1; i <= m; i++){
for(int j = 1; j <= n; j++){
int temp = dp[i-1][j-1];
path[i][j] = j-1;
if(dp[i-1][j] <= temp){
temp = dp[i-1][j];
path[i][j] = j;
}
if(dp[i-1][j+1] <= temp){
temp = dp[i-1][j+1];
path[i][j] = j+1;
}
dp[i][j] += temp;
}
}
int minn = INF,id;
for(int i = 1; i <= n; i++){
if(minn >= dp[m][i]){
minn = dp[m][i];
id = i;
}
}
printf("Case %d\n",++cas);
ans[0] = id;
int cnt = 1;
for(int i = m; i > 1; i--){
id = path[i][id];
ans[cnt++] = id;
}
for(int i = cnt-1; i >= 0; i--){
printf("%d%c",ans[i],i == 0 ? '\n' : ' ');
}
}
return 0;
}