题意:给一个n行m列(n<=10,m<=100)的整数矩阵,从第一列任何一个位置出发每次往右或右上或右下走一格,最终到达最后一列。要求经过的整数之和最小。整个矩阵是环形的,即第一行的上一行是最后一行,最后一行的下一行是第一行。输出路径上每列的行号。多解时输出字典序最小的。
思路:设d(i,j)为从格子(i,j)出发到最后一列的最小开销。在计算d(i,j)的同时记录“下一列的行号”的最小值(在满足最优解的前提下)
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mod 1000000007;
using namespace std;
const int maxn = 102;
int n,m,nex[12][maxn];
int d[12][maxn],a[12][maxn];
int main()
{
while(scanf("%d%d",&n,&m) != EOF)
{
int first = 0,ans = INF;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
scanf("%d",&a[i][j]);
}
for(int i = m - 1; i >= 0; i--) //列
{
for(int j = 0; j < n; j++) //行
{
if(i == m - 1)
{
d[j][i] = a[j][i];
}
else
{
d[j][i] = INF;
int row[] = {j - 1,j,j + 1};
if(j == 0) row[0] = n - 1;
if(j == n - 1) row[2] = 0;
sort(row,row + 3);
for(int k = 0; k < 3; k++)
{
int v = d[row[k]][i + 1] + a[j][i];
if(v < d[j][i])
{
d[j][i] = v;
nex[j][i] = row[k];
}
}
}
if(i == 0 && d[j][i] < ans)
{
ans = d[j][i];
first = j;
}
}
}
printf("%d",first + 1);
for(int i = nex[first][0],j = 1; j < m; i = nex[i][j],j++)
{
printf(" %d",i + 1);
}
printf("\n%d\n",ans);
}
return 0;
}