您的位置:首页 > 其它

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的情况

#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,希望能再熟练学习,更好运用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ACM