题意:给定一个n*m的矩阵,要求从第一列的任何一行出发,每次沿右或右下或右上到达后面一列,最后到第m列,要求经过的整数之和最小,如果多解要求是解的行字典序最小的。矩阵是环形的,即第一行的上一行是最后一行,最后一行的下一行是第一行。
思路:
逆向递推。定义状态:d[ i ][ j ] 为(i , j )到最后一列的最小和。边界条件是最后一列,d[ i ][ n] = table[ i ][ n ], 1 <= i <= n;从后往前,依次枚举当前列,每次有 3 种决策,右,右上,右下,选择代价最小的并记录路径。路径记录用 next[ i ][ j ] 表示(i ,j)的下一个决策行,如; next[ 2][ 1 ] = 3;说明(2,1)的下一个最优决策是(3,2)。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 1<<30;
int table[11][105] , d[11][105] , nextr[11][105];
int main()
{
int r,c;
//freopen("in.txt","r",stdin);
while(scanf("%d %d",&r,&c) == 2){
for(int i = 1; i <= r; ++i)
for(int j = 1; j <= c; ++j)
scanf("%d",&table[i][j]);
int ans = INF, first;
for(int j = c; j >= 1; --j){
for(int i = 1; i <= r; ++i){
if(j == c) d[i][j] = table[i][j]; // 边界条件
else {
int rows[3] = { i-1, i, i+1 };
if(i == 1) rows[0] = r;
if(i == r) rows[2] = 1;
sort(rows,rows+3);
d[i][j] = INF;
for(int k = 0; k < 3; ++k){
int v = d[rows[k]][j+1] + table[i][j];
if(v < d[i][j]) { d[i][j] = v; nextr[i][j] = rows[k]; }
}
}
if(j == 1&&ans > d[i][j]) { ans = d[i][j]; first = i; }
}
}
printf("%d",first);
int i,j;
for(i = nextr[first][1], j = 2; j <= c; i = nextr[i][j], ++j){
printf(" %d",i);
//if(j < c) printf(" ");
}
printf("\n%d\n",ans);
}
return 0;
}