动态规划--多段图的最短路--单向TSp(Unidirectional TSP ,UVa 116)

【问题描述】

  给定一个M行、N列的数字矩阵,你需要写一个程序计算一条从左到右走过矩阵且权和最小的路径。一条路径可以从第一列的任意位置出发,到达第N列的任意位置。每一步为从第i列走到第i+1列的相邻行(水平移动或沿45度斜线移动)。第一行和最后一行看作是相邻的,即你应当把这个矩阵看成是一个卷起来的圆筒。如下为合法的走法: 
                 
  路径的权和为所有经过的N个方格中整数的和。

  两个略有不同的5*6矩阵的最小路径如下。只有矩阵中最下面一行的数不同。右边矩阵的路径利用了第一行与最后一行相邻的性质。 
     

【输入格式】

  第一行为两个整数M和N,分别表示矩阵的行数和列数。 
  接下来的M行,每行N个正整数,其中第i行第j列的整数表示矩阵的第i行第j列的元素。

【输出格式】

  第一行为最小权和的路径,第二行为该路径的权和。路径由N个整数组成(相邻整数间用一个空格分开),表示路径经过的行号。如果权和最小的路径不止一条,输出字典序最小的一条。

【输入样例】

5 6 
3 1 1 2 8 6 
6 1 8 2 7 4 
5 9 3 9 9 5 
8 4 1 3 2 6 
3 7 3 8 6 4

【输出样例】

1 1 5 4 4 5 
16

【数据范围】

对于30%的数据有:N,M<=10; 
对于50%的数据有:N,M<=100; 
对于100%的数据有:N,M<=1000,矩阵的每个元素为1..100之间的数;

这道题难点就在输出路径(还要字典序最小),我开始想到了用fa数组来记录,有3个点可以到达该点,就在这三个点中取权值和最小的(一样就取字典序小的),但这样不难发现我们是在保证逆字典序最小,所以倒着来,改成从m列走到第一列就可以保证字典序最小了。 

下面是根据 算法竞赛--入门经典写的代码:

#include<iostream>

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3fffffff  //新学会的定义比较大的数
using namespace std;


int mapp[15][105];//存储图
int dp[15][105];  //记录从出发点到终点的最小的开销
int nexts[15][105]; //记录下个方向
int m,n; //定义图的大小


int main()
{
    while(~scanf("%d%d",&m,&n))
    {
        int ans=INF;//记录步数的中间变量;
        int first=0;//记录第一列的最终行数
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
            scanf("%d",&mapp[i][j]);//读入图;
        for(int j=n-1;j>=0;j--)//从最后一行开始递推
        {
            for(int i=0;i<m;i++)
            {
                if(j==n-1)
                //处理列的关系
                    dp[i][j]=mapp[i][j];//将最后一列的值赋给dp[][];
                else
                {
                    int rows[3]={i,i-1,i+1};
                    //处理行的关系
                    if(i==m-1)
                        rows[2]=0;//让第m-1的下一行为第0行;
                    else if(i==0)
                        rows[1]=m-1;//让第0行的上一行为第m-1行;
                    sort(rows,rows+3);
                    dp[i][j]=INF;
                    //开始选择方向与记录步数
                    for(int k=0;k<3;k++)
                    {
                        int v=dp[rows[k]][j+1]+mapp[i][j];//找一个值作为步数的初值;与dp[][]进行比较
                        if(v<dp[i][j])
                        {
                            dp[i][j]=v;
                            nexts[i][j]=rows[k];
                        }//更新dp[][]的最小值,并且记录下个方向;
                    }
                }
                if(j==0&&dp[i][j]<ans)
                {
                    ans=dp[i][j];
                    first=i;
                }//第一列的时候ans保存最小的路径,first保存第一列行号
            }
        }
        printf("%d ",first+1);//输出第一列的行数
        for(int i=nexts[first][0],j=1;j<n;i=nexts[i][j],j++)//输出其他的行数
            printf("%d ",i+1);    
        printf("\n%d\n",ans);  //输出总值;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值