【动态规划】Unidirectional TSP 单向旅行商问题

给定一个m行n列(m<=10,n<=100)的整数矩阵,从第一列任何一个位置出发每次往右、右上或者右下走一格,最终到达最后一列。要求经过的整数之和最小。整个矩阵是环形的,即第一行的上一行是最后一行,最后一行的下一行是第一行。输出路径上每列的行号。多解时输出字典序最小的。图9-5中是两个矩阵和对应的最优路径(唯一的区别是最后一行)。
图9-5
图9-5

Input sample

5 6
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
5 6
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 1 2 3
2 2
9 10 9 10

Output sample

1 2 3 4 4 5
16
1 2 1 5 4 5
11
1 1
19

————————————————分割の线——————————————

分析

总算是一道偏水的题目了……
已知到点(i,j)的值的大小与后续的路径没有关系,所以可以定义f[i][j]表示到点(i,j)的最小值;
转移方程如下
f[i][j]=min(f[i+1][j-1],f[i+1][j],f[i+1][j+1])+a[i][j]
小细节:由于是环形的矩阵,所以要对(j-1)和(j+1)做一个处理。处理完后,对三个位置进行排序,在值相同时,求最小位置。
关于路径,可以运用opt[i][j]储存然后顺序输出即可。
代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int a[12][102];//保存点的数值
int f[12][102];//保存最小值(最优状态)
int vis[12][102];//标记是否做过该点
int opt[12][102];//保存操作
int n,m;
int change(int x)//完成矩阵到环形的转换
{
    while(x<1) x+=n;
    while(x>n)x-=n;
    return x;
}
void print(int x,int y)//递归输出路径,貌似用while也可以
{
    if(y==m) printf("%d",x);
    else
    {
        printf("%d ",x);
        print(opt[x][y],y+1);
    }
    return ;
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&a[i][j]);
        memset(f,-1,sizeof(f));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++)
            f[i][m]=a[i][m],vis[i][m]=1;
        for(int i=m-1;i>=1;i--)//逆推,方便求最小字典序的路径
            for(int j=1;j<=n;j++)
            {
                int b[3];
                b[0]=change(j-1);
                b[1]=change(j);
                b[2]=change(j+1);
                sort(b,b+3);//排序以保证到该店的路径最小
                if(vis[j][i]==0||f[j][i]>f[b[0]][i+1]+a[j][i])
                {
                    vis[j][i]=1;
                    f[j][i]=f[b[0]][i+1]+a[j][i];
                    opt[j][i]=b[0];
                }
                if(vis[j][i]==0||f[j][i]>f[b[1]][i+1]+a[j][i])
                {
                    vis[j][i]=1;
                    f[j][i]=f[b[1]][i+1]+a[j][i];
                    opt[j][i]=b[1];
                }
                if(vis[j][i]==0||f[j][i]>f[b[2]][i+1]+a[j][i])
                {
                    vis[j][i]=1;
                    f[j][i]=f[b[2]][i+1]+a[j][i];
                    opt[j][i]=b[2];
                }
            }
        int ans=0x7fffffff,pos=0;
        for(int i=1;i<=n;i++)
        {
            if(f[i][1]<ans)
            {
                ans=f[i][1];
                pos=i;//记录路径起点和最小值
            }
        }
        print(pos,1);//输出路径
        cout<<endl<<ans<<endl;//输出最小值
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyc1719/article/details/80003019