HDU 5402 Travelling Salesman Problem (2015 Multi-University Training Contest 9 2015多校联合)

本文探讨了一道迷宫路径最值问题,旨在找到从左上角到右下角的路径,使得路径上的数字总和最大。文章分析了不同迷宫尺寸下的最优路径策略,并给出了详细的解决方案。

题目传送门:HDU5402 Travelling Salesman Problem

Travelling Salesman Problem

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 522    Accepted Submission(s): 181
Special Judge


Problem Description
Teacher Mai is in a maze with  n  rows and  m  columns. There is a non-negative number in each cell. Teacher Mai wants to walk from the top left corner  (1,1)  to the bottom right corner  (n,m) . He can choose one direction and walk to this adjacent cell. However, he can't go out of the maze, and he can't visit a cell more than once.

Teacher Mai wants to maximize the sum of numbers in his path. And you need to print this path.
 

Input
There are multiple test cases.

For each test case, the first line contains two numbers  n,m(1n,m100,nm2) .

In following  n  lines, each line contains  m  numbers. The  j -th number in the  i -th line means the number in the cell  (i,j) . Every number in the cell is not more than  104 .
 

Output
For each test case, in the first line, you should print the maximum sum.

In the next line you should print a string consisting of "L","R","U" and "D", which represents the path you find. If you are in the cell  (x,y) , "L" means you walk to cell  (x,y1) , "R" means you walk to cell  (x,y+1) , "U" means you walk to cell  (x1,y) , "D" means you walk to cell  (x+1,y) .
 

Sample Input
  
3 3 2 3 3 3 3 3 3 3 2
 

Sample Output
  
25 RRDLLDRR
 

Author
xudyh
 

Source


因为在队里我比较擅长DFS BFS走迷宫这样的题,所以看到这题果断自己接手了。读题发现并不是走迷宫啊- -和DFS BFS也没什么关系啊!就是从左上角走到右下角,尽可能使走过的路加起来的数字和最大。类似吃豆子,我们要想玩贪吃蛇那样 争取把格子里面的东西都吃了。那么由此我就得出了4种情况,行列分别为奇数奇数、奇数偶数、偶数奇数、偶数偶数。在纸上画一画,发现其实分为3种情况,上图!(诅咒吐槽我画的图的人找不到对象→_→)

可以发现:行列分别为奇数奇数、奇数偶数的是一类,对应图1、2,可以用RRRDLLLDRRR这样的方式实现。 第二类是行列为偶数奇数,对应图3,可以用DDDDRLLLLRDDD的方式实现。可以发现,只要行列不全为偶数,那么我们就可以走完整个图,输出的数字也就是整个格子所有数字之和。第三类对应图4,也就是行列都为偶数。这样的话我们发现,格子并不能全走完。接下来我们就要讨论了。

画一个4x4的格子,我们可以发现,在这种情况里,总是要有一个格子走不到,要被抛弃。那么我们画一画,到底哪些点是可以被抛弃的。画了之后发现,奇数行的第偶数个格子和偶数行的第奇数个格子都是可以被抛弃的。如果不好理解的话,我们把格子看错黑白棋的棋盘,如果左上角为黑色的话,那么所有白色格子都是可以被抛弃的那一个格子的位置。为了加和最大,我们只需要找这些白色格子里面数字最小的那个,用x y记录它的位置。那么我们要输出的sum和就是sum-min,(或者说sum-a[x][y])

接下来就是走法了,方法是这样:在走到最小值格子所在行或者上一行之前,我们采用图1、2的走法,也就是RRRDLLLDRRRDLLLD,走到格子所在的行或者前一行时,我们采用图3走法,来搞定这两行,即RDRURDRU,如果走到最小值格子所在的点,我们只需绕过它即可,比如URDRRURDR。喏,只需要在走到格子那里是不再U或者D经过它,而是改成R,绕过它。看图:


到这里就解决啦。附上出题人解题报告:


下面附上AC代码:

#include <cstdio>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <cstring>
using namespace std;
#define TEST cout<<"wwwwwwwwww"<<endl
int maze[105][105];
int main()
{
    int n,m,sum;
    int min;
    int i,j;
    int x,y;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        min=0x7f7f7f7f;
        sum=0;
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=m; j++)
            {
                scanf("%d",&maze[i][j]);
                sum+=maze[i][j];
                if(m%2==0 && n%2==0)
                {
                    if(i%2==0 && j%2!=0)
                    {
                        if(maze[i][j]<min)
                        {
                            min=maze[i][j];
                            x=i;
                            y=j;
                        }
                    }

                    if(i%2!=0 && j%2==0)
                    {
                        if(maze[i][j]<min)
                        {
                            min=maze[i][j];
                            x=i;
                            y=j;
                        }
                    }
                }
            }
        }

        if(n%2!=0)
        {
            printf("%d\n",sum);
            for(i=1;i<=n;i++)
            {
                if(i%2!=0)
                {
                    for(j=1;j<=m-1;j++)
                        printf("R");
                }
                if(i%2==0)
                {
                    for(j=1;j<=m-1;j++)
                        printf("L");
                }
                if(i==n && j==m)
                    break;
                else
                    printf("D");
            }
        }
        else if(m%2!=0)
        {
            printf("%d\n",sum);
            for(i=1;i<=m;i++)
            {
                if(i%2!=0)
                {
                    for(j=1;j<=n-1;j++)
                        printf("D");
                }
                if(i%2==0)
                {
                    for(j=1;j<=n-1;j++)
                        printf("U");
                }
                if(i==m && j==n)
                    break;
                else
                    printf("R");
            }
        }
        else
        {
            printf("%d\n",sum-min);
            for(i=1;i<=n;i+=2)
            {
                if(x==i || x==i+1)
                {
                    for(j=1;j<y;j++)
                    {
                        if(j%2!=0)
                            printf("D");
                        else
                            printf("U");
                        printf("R");
                    }
                    if(y<m)
                        printf("R");
                    for(j=y+1;j<=m;j++)
                    {
                        if(j%2!=0)
                            printf("U");
                        else
                            printf("D");
                        if(j<m)
                            printf("R");
                    }
                    if(i<n-1)
                        printf("D");
                }
                else if(x<i)
                {
                    for(j=1;j<m;j++)
                        printf("L");
                    printf("D");
                    for(j=1;j<m;j++)
                        printf("R");
                    if(i<n-1)
                        printf("D");
                }
                else
                {
                    for(j=1;j<m;j++)
                        printf("R");
                    printf("D");
                    for(j=1;j<m;j++)
                        printf("L");
                    printf("D");
                }
            }
        }
    printf("\n");
    }
    return 0;
}
重要的话说三遍:不要吐槽我的图!不要吐槽我的图!不要吐槽我的图! QQ截图截了个白色地方鼠标画的!!!!!鼠绘啊有木有!!!!这行字加特技!

### DualOpt 算法及其在大规模旅行商问题中的应用 DualOpt 算法是一种用于求解旅行商问题(Traveling Salesman Problem, TSP)的局部搜索算法,其核心思想是通过双重优化策略逐步改进路径,以达到更优的解。该算法属于2-opt类方法的一种扩展形式,通常用于解决单仓库和仓库的大规模TSP问题,尤其适用于需要高效计算近似最优解的场景。 DualOpt 的基本操作方式是:从一个初始路径出发,反复选择两个边并交换它们的位置,从而生成新的路径,并判断是否能够缩短总路径长度。与传统的2-opt算法相比,DualOpt 会引入额外的优化规则或启发式机制,例如动态调整搜索方向、结合贪婪策略等,以提升收敛速度和解的质量[^1]。 在大规模旅行商问题中,DualOpt 算法常与其他元启发式算法结合使用,如遗传算法(GA)、模拟退火(SA)或禁忌搜索(TS),形成混合优化策略。这种组合可以利用 DualOpt 的局部搜索能力对全局搜索结果进行精细化处理,从而在有限时间内获得高质量的解。例如,在分支定价法求解仓库旅行商问题(LS-MDMTSP)后,可将 DualOpt 应用于每个子路径的优化阶段,进一步压缩路径成本[^1]。 此外,DualOpt 算法也可以嵌入到智能群体优化算法中,如离散浣熊优化算法(DCOA)。在这种情况下,DualOpt 可作为个体路径的局部优化工具,提升整个种群的搜索效率和解的稳定性。例如,在每次迭代后对当前最优路径执行 DualOpt 操作,有助于避免陷入局部最优并加速收敛过程[^2]。 以下是一个简化的 DualOpt 算法实现示例(Python): ```python def dual_opt(tsp_path): n = len(tsp_path) improved = True while improved: improved = False for i in range(n - 2): for j in range(i + 2, n): if j - i == 1: continue # 计算原路径和新路径的距离差 original_cost = distance(tsp_path[i], tsp_path[i+1]) + distance(tsp_path[j], tsp_path[(j+1) % n]) new_cost = distance(tsp_path[i], tsp_path[j]) + distance(tsp_path[i+1], tsp_path[(j+1) % n]) if new_cost < original_cost: tsp_path[i+1:j+1] = tsp_path[i+1:j+1][::-1] improved = True return tsp_path def distance(a, b): return ((a[0] - b[0])**2 + (a[1] - b[1])**2)**0.5 ``` 上述代码展示了如何基于 DualOpt 原理对路径进行局部翻转操作,以寻找更短的路径方案。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值