• 07_旅行商问题(TSP问题,货郎担问题,经典NPC难题)


    问题来源:刘汝佳《算法竞赛入门经典--训练指南》 P61 问题9:

    问题描述:有n(n<=15)个城市,两两之间均有道路直接相连,给出每两个城市i和j之间的道路长度L[i][j],求一条经过每个城市一次且仅一次,最后回到起点的路线,使得经过的道路总长度最短(城市编号为0~n-1)。

    分析: 1.因为最后走的路线为一个环,可以设城市0为起点城市。

        2.将每个城市看作二进制的一个位(1代表有,0代表没有),则数k可以表示一些城市的集合(例如k=13,二进制表示为1101,表示城市0,2,3的集合),我们可以求得k<=2^15-1,令  aim=2^15-1;

        3.dp[k][j]表示经过了k集合中的所有城市并且以j城市为终点的路径的最小值

        则dp[k][j] = Min{dp[k][j],dp[k-j][i]+dis[i][j] | (0<=i<=n-1 && i属于集合k)};(其中k-j表示集合k中去掉数j后的集合(所以j应该是集合k中的元素)),

    例题链接:http://acm.fzu.edu.cn/problem.php?pid=2186

    例题: fzu 2186

    Problem 2186 小明的迷宫

    Accept: 88    Submit: 270
    Time Limit: 1000 mSec    Memory Limit : 32768 KB

     Problem Description

    小明误入迷宫,塞翁失马焉知非福,原来在迷宫中还藏着一些财宝,小明想获得所有的财宝并离开迷宫。因为小明还是学生,还有家庭作业要做,所以他想尽快获得所有财宝并离开迷宫。

     Input

    有多组测试数据。

    每组数据第一行给出两个正整数n,m(0<n,m<=100)。代表迷宫的长和宽。

    接着n行,每行m个整数。正数代表财宝(财宝的个数不超过10);负数代表墙,无法通过;0代表通道。

    每次移动到相邻的格子,所花费的时间是1秒。小明只能按上、下、左、右四个方向移动。

    小明的初始位置是(1,1)。迷宫的出口也在(1,1)。

     Output

    输出获得所有财宝并逃出迷宫所花费的最小时间,如果无法完成目标则输出-1。

     Sample Input

    3 3
    0 0 0
    0 100 0
    0 0 0
    2 2
    1 1
    1 1

     Sample Output

    4
    4

     Source

    FOJ有奖月赛-2015年03月
    思路:先将出口和宝藏之间的距离全部通过DFS求出来(出口的地方需要特殊判断和处理),接下来就是裸的TSP问题dp+状压
      1 #include "stdio.h"
      2 #include "string.h"
      3 #include "queue"
      4 using namespace std;
      5 #define N 105
      6 #define INF 0x3fffffff
      7 
      8 int m,n;
      9 int map[N][N],mark[N][N];
     10 int dis[15][15],flag[15],dist[15];
     11 int dir[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};
     12 
     13 int dp[5050][15],b[15];
     14 int inline Min(int a,int b){ return a<b?a:b; }
     15 
     16 struct Point
     17 {
     18     int x,y;
     19 } point[15];
     20 
     21 struct node
     22 {
     23     int x,y;
     24     int length;
     25 };
     26 
     27 void DFS(int start,int x,int y,int length)
     28 {
     29     int i,k;
     30     int x1,y1;
     31     queue<node> q;
     32     node cur,next;
     33     cur.x = x;
     34     cur.y = y;
     35     cur.length = 0;
     36     q.push(cur);
     37     while(!q.empty())
     38     {
     39         cur = q.front();
     40         q.pop();
     41         for(i=0; i<4; i++)
     42         {
     43             next.x = x1 = cur.x+dir[i][0];
     44             next.y = y1 = cur.y+dir[i][1];
     45             next.length = cur.length+1;
     46             if(x1<=0 || x1>n || y1<=0 || y1>m || mark[x1][y1]!=0 || map[x1][y1]<0)
     47                 continue;
     48             if(map[x1][y1]>0)
     49             {
     50                 k = map[x1][y1];
     51                 dis[start][k] = dis[k][start] = next.length;
     52             }
     53             mark[x1][y1] = 1;
     54             q.push(next);
     55         }
     56     }
     57 }
     58 
     59 int main()
     60 {
     61     int ans;
     62     int i,j,k,num,l;
     63     b[0] = 1;
     64     for(i=1; i<15; i++)  //b[i]存的2^i
     65         b[i] = b[i-1]*2;
     66     while(scanf("%d %d",&n,&m)!=EOF)
     67     {
     68         num = 2;
     69         for(i=1; i<=n; ++i)
     70         {
     71             for(j=1; j<=m; j++)
     72             {
     73                 scanf("%d",&map[i][j]);
     74                 if(i==1 && j==1&& map[i][j]>0) num--;
     75                 if(map[i][j]>0)      map[i][j]= num++;
     76                 else if(map[i][j]<0) map[i][j]=-1;
     77             }
     78         }
     79         if(map[1][1]<0)
     80         {
     81             printf("-1
    ");
     82             continue;
     83         }
     84         map[1][1] = 1;
     85         for(i=1; i<=n; ++i)
     86         {
     87             for(j=1; j<=m; j++)
     88             {
     89                 if(map[i][j]>0)
     90                 {
     91                     k = map[i][j];
     92                     point[k].x = i, point[k].y = j;
     93                 }
     94 
     95             }
     96         }
     97         for(i=1; i<num; i++)
     98         {
     99             for(j=1; j<num; j++)
    100                 dis[i][j] = INF;
    101             dis[i][i]= 0;
    102         }
    103         for(i=1; i<num; i++)  //以每个点为起点广搜,求出任意两点间的最短距离
    104         {
    105             memset(mark,0,sizeof(mark));
    106             mark[point[i].x][point[i].y] = 1;
    107             DFS(i,point[i].x,point[i].y,0);
    108         }
    109         num--;  //宝藏编号为1~num
    110         int tt = 0;
    111         for(i=2; i<=num; i++)
    112         {
    113             if(dis[1][i]==INF)  //只要存在一个INF,表示至少有一个宝藏不可达,输出-1
    114                 tt = 1;
    115         }
    116         if(tt==1)
    117         {
    118             printf("-1
    ");
    119             continue;
    120         }
    121         int aim =b[num+1]-2;//y因为从2^1开始到2^num,则aim = 2^(num+1)-1-2^0
    122         for(i=0; i<=aim; i++)
    123         {
    124             for(j=0; j<=num; j++)
    125                 dp[i][j] = INF;
    126         }
    127         dp[2][1] = 0; 
    128         for(l=2; l<=aim; l++) //一个集合l
    129         {
    130             for(i=1; i<=num; i++)
    131             {
    132                 for(j=1; j<=num; j++)
    133                 {
    134                     if(i==j) continue;
    135                     if((b[i]&l)==0) continue;  //必须满足i  在集合l中,不满足,跳过,
    136                     if((b[j]&l)==1) continue;  //必须满足j不在集合l中,不满足,跳过,
    137                     if(dp[l][i]==INF) continue;
    138                      dp[l|b[j]][j]=Min(dp[l|b[j]][j],dp[l][i]+dis[i][j]);
    139                 }
    140             }
    141         }
    142         ans = INF;
    143         for(i=2; i<=num; i++)
    144             ans = Min(ans,dp[aim][i]+dis[i][1]);
    145         printf("%d
    ",ans);
    146     }
    147     return 0;
    148 }
  • 相关阅读:
    MutationObserver DOM变化的观察
    lspci详解分析
    dpdk快速编译使用
    bonding的系统初始化介绍
    fio测试nvme性能
    [驱动] 一个简单内核驱动,通过qemu调试(1)
    qemu启动vm后,如何host上使用ssh连接?
    Linux C下变量和常量的存储的本质
    从计算机中数据类型的存储方式,思考理解原码,反码,补码
    Linux C动态链接库实现一个插件例子
  • 原文地址:https://www.cnblogs.com/ruo-yu/p/4385291.html
Copyright © 2020-2023  润新知