大致题意:
从左到右寻找最短路径,按字典序输出路径以及路径长。
思路:
从右往左每两列一看,计算局部最优解,记录在数组dp中;
例:
5行6列的Map
3 4 1 2 8 6
6 1 8 2 7 4
5 9 3 9 9 5
8 4 1 3 2 6
3 7 2 8 6 4
对应dp
一:
0 0 0 0 0 6
0 0 0 0 0 4
0 0 0 0 0 5
0 0 0 0 0 6
0 0 0 0 0 4
二:
0 0 0 0 12 6
0 0 0 0 11 4
0 0 0 0 13 5
0 0 0 0 6 6
0 0 0 0 10 4
这里的12是4 6 4三者最小(4)加上Map对应位置元素值(8)
这里的11是6 4 5三者最小(4)加上Map对应位置元素值(7)
…
这里的10是6 4 6三者最小(4)加上Map对应位置元素值(6)
不难看出,dp中存储每两列的局部最优解,然后我们可以把这两列看成一列,同样地做法往前递推,即可得到整个Map的最优解,也就是dp第一列中最小的元素
写出状态转移方程:
dp[i][j] = mm + map[i][j]
mm为dp[i][j]向右所指的三个子元素的最小值
一般情况为dp[i-1][j+1], dp[i][j+1], dp[i+1][j+1]
特别考虑i = 1和i = m的情况
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#define MAX 102
using namespace std;
int map[MAX][MAX];
int dp[MAX][MAX];
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int m, n;
int i, j;
int mm, ans, route;
//mm记录dp向右所指三个子元素的最小值
//ans记录最短路径长
//route标记路径
while(~scanf("%d%d", &m, &n))
{
memset(map, 0, sizeof(map));
memset(dp, 0, sizeof(dp));
//输入map
for(i = 1; i <= m; i++)
for(j = 1; j <= n; j++)
scanf("%d", &map[i][j]);
//计算mm, dp, 从右往左按列递推
for(j = n; j >= 1; j--)
for(i = 1; i <= m; i++)
{
if(i == 1)
mm = min( min(dp[m][j+1], dp[1][j+1]), dp[2][j+1] );
else if(i == m)
mm = min( min(dp[m-1][j+1], dp[m][j+1]), dp[1][j+1] );
else
mm = min( min(dp[i-1][j+1], dp[i][j+1]), dp[i+1][j+1] );
//状态转移方程
dp[i][j] = mm + map[i][j];
}
//1<<30意思是开了一个很大的数
ans = 1<<30;
//找dp第一列最小元素,即最短路径长,并标记位置route
for(i = 1; i <= m; i++)
if(dp[i][1] < ans)
{
ans = dp[i][1];
route = i;
}
printf("%d", route);
//寻找子route(从左往右根据dp三个子元素的最小确定)
for(j = 2; j <= n; j++)
{
if(route == 1)
{
if(dp[1][j] <= dp[m][j] && dp[1][j] <= dp[2][j])
{
route = 1;
printf(" %d", route);
}
else if(dp[2][j] <= dp[m][j] && dp[2][j] <= dp[1][j])
{
route = 2;
printf(" %d", route);
}
else
{
route = m;
printf(" %d", route);
}
}
else if(route == m)
{
if(dp[1][j] <= dp[m][j] && dp[1][j] <= dp[m-1][j])
{
route = 1;
printf(" %d", route);
}
else if(dp[m-1][j] <= dp[m][j] && dp[m-1][j] <= dp[1][j])
{
route = m-1;
printf(" %d", route);
}
else
{
route = m;
printf(" %d", route);
}
}
else
if(dp[route-1][j] <= dp[route][j] && dp[route-1][j] <= dp[route+1][j])
{
route = route-1;
printf(" %d", route);
}
else if(dp[route][j] <= dp[route-1][j] && dp[route][j] <= dp[route+1][j])
printf(" %d", route);
else
{
route = route+1;
printf(" %d", route);
}
}
printf("\n%d\n", ans);
}
return 0;
}
缺点及改进:
代码段过于冗长,最好把各个重复的功能段独立成功能函数,简化代码。
最后输出路径的方法不太好,可以在计算dp的时候直接计算路径。
例:http://blog.csdn.net/z309241990/article/details/8617785
不过我感觉效率上面不差太多,我这样写还更好懂一些。
小结:
dp算法,关键代码就一两行,然而要弄懂整个流程并不容易,这题还算一道简单dp,希望能再熟练学习,更好运用。