HDU-1619 Unidirectional TSP dp
2015-07-18 12:02
218 查看
大致题意:
从左到右寻找最短路径,按字典序输出路径以及路径长。
思路:
从右往左每两列一看,计算局部最优解,记录在数组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的情况
缺点及改进:
代码段过于冗长,最好把各个重复的功能段独立成功能函数,简化代码。
最后输出路径的方法不太好,可以在计算dp的时候直接计算路径。
例:http://blog.csdn.net/z309241990/article/details/8617785
不过我感觉效率上面不差太多,我这样写还更好懂一些。
小结:
dp算法,关键代码就一两行,然而要弄懂整个流程并不容易,这题还算一道简单dp,希望能再熟练学习,更好运用。
从左到右寻找最短路径,按字典序输出路径以及路径长。
思路:
从右往左每两列一看,计算局部最优解,记录在数组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,希望能再熟练学习,更好运用。
相关文章推荐