题意:
如图,从左到右走,每次可以往左上(up)左(fo)左下(dn)走,当走到第一行或最后一行时可以如图穿越。
求一条权值最小的路径,并打印字典序最小的路径。
解析:
状态转移方程:
dp[ i ][ j ] = min( dp[ (i - 1 + m) % m ] [ j + 1 ], //up
dp[ i ] [ j + 1], //fo
dp[ ( i + 1) % m ] [ j + 1] ) //dn
字典序最小的路径也在记忆化搜索中更新,详见代码。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
using namespace std;
const int maxr = 10 + 10;
const int maxc = 100 + 10;
const int inf = 0x3f3f3f3f;
int g[maxr][maxc];
int path[maxr][maxc];
int dp[maxr][maxc];
int m, n;
int dfs(int i, int j)
{
if (n <= j)
return 0;
if (dp[i][j] != inf)
return dp[i][j];
int up = dfs((i - 1 + m) % m, j + 1);
int fo = dfs(i, j + 1);
int dn = dfs((i + 1) % m, j + 1);
int minn = up;
if (fo < minn)
minn = fo;
if (dn < minn)
minn = dn;
int dir = inf;
if (minn == up)
dir = min(dir, (i - 1 + m) % m);
if (minn == fo)
dir = min(dir, i);
if (minn == dn)
dir = min(dir, (i + 1) % m);
dp[i][j] = minn + g[i][j];
path[i][j] = dir;
return dp[i][j];
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
while (scanf("%d%d", &m, &n) != EOF)
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
scanf("%d", &g[i][j]);
dp[i][j] = inf;
}
}
int ans = inf;
int pos = -1;
for (int i = 0; i < m; i++)
{
int t = dfs(i, 0);
if (t < ans)
{
ans = t;
pos = i;
}
}
printf("%d", pos + 1);
pos = path[pos][0];
for (int i = 1; i < n; i++)
{
printf(" %d", pos + 1);
pos = path[pos][i];
}
printf("\n%d\n", ans);
}
return 0;
}