旅行商模型

旅行商问题(Traveling Saleman Problem,TSP)又译为旅行推销员问题货郎担问题,简称为TSP问题,是最基本的路线问题,该问题是在寻求单一旅行者由起点出发,通过所有给定的需求点之后,最后再回到原点的最小路径成本。

HDU 4568

     http://acm.hdu.edu.cn/showproblem.php?pid=4568

进出一次,找到所有能找到的宝藏,裸的TSP问题。将整个边界理解成一个点,找到每两个宝藏之间的最短距离和每个宝藏的离边界点的最短距离。

先用spfa预处理出宝藏与宝藏之间的最短距离,宝藏到边界的最短距离,然后就是经典的求TSP过程了。


const   int  Max_N = 208 ;
const   int  Max_K = 15 ;
const   int  inf = (1<<30) ;

struct  Node{
        int x ;
        int y ;
        int step ;
        friend bool operator < (const Node &A , const Node &B){
             return A.step > B.step ;
        }
        Node(){}
        Node(int i , int j , int k):x(i) , y(j) ,step(k){}
};

struct  BaoZhang{
        int  x ;
        int  y ;
};


BaoZhang Bao[Max_K] ;
int Bao_Id[Max_N][Max_N] ;
int  N , M , K ;
int  money[Max_N][Max_N] ;
bool visited[Max_N][Max_N] ;
int  d[4][2] = { {1,0} ,{-1 ,0} ,{0 ,-1} ,{0 ,1} } ;
int  dist[Max_K][Max_K] ;
int  dp[1<<Max_K][Max_K] ;

int  cango(int x , int y){
     return 1 <= x && x <= N && 1 <= y && y <= M ;
}

void  bfs(BaoZhang b , int u){
      priority_queue<Node> que ;
      memset(visited , 0 , sizeof(visited)) ;
      que.push(Node(b.x , b.y , 0)) ;
      visited[b.x][b.y] = 1 ;
      int OK_dot = 0 ;
      while(! que.empty()){
           Node now = que.top() ;
           que.pop() ;
           if(Bao_Id[now.x][now.y] > 0){
                int v = Bao_Id[now.x][now.y] ;
                dist[u][v] = now.step ;
                OK_dot++ ;
           }
           if(dist[u][0] == inf && (now.x == 1 || now.x == N || now.y == 1 || now.y == M) ){
                 dist[u][0] = now.step ;
                 dist[0][u] = now.step + money[b.x][b.y] ;
                 OK_dot++ ;
           }
           if(OK_dot == K+1)
                 return  ;
           for(int i = 0 ; i < 4 ; i++){
                 int x = now.x + d[i][0] ;
                 int y = now.y + d[i][1] ;
                 if(cango(x,y) && money[x][y] != -1 && !visited[x][y]){
                       visited[x][y] = 1 ;
                       que.push(Node(x,y,now.step+money[x][y])) ;
                 }
           }
      }
}

int  TSP(int n){
     int i , j , k , limit ;
     dist[0][0] = 0 ;
     limit = 1<<(n+1) ;
     for(i = 0 ; i < limit ; i++)
        for(j = 0 ; j <= n ; j++)
            dp[i][j] =  inf ;
     for(i = 0 ; i <= n  ; i++)
        dp[1<<i][i] = dist[0][i] ;
     for(i = 0 ; i < limit ; i++){
        for(j = 0 ; j <= n ; j++){
           if(i & (1<<j) == 0)  continue ;
           for(k = 0 ; k <= n ; k++){
                if(i & (1<<k) == 0)  continue ;
                if(dist[k][j] == inf) continue ;
                dp[i][j]  = min(dp[i][j], dp[i^(1<<j)][k] + dist[k][j]) ;
           }
        }
    }
    return dp[limit-1][0] ;
}


int  main(){
     int T , i , j , k;
     scanf("%d" ,&T) ;
     while(T--){
          scanf("%d%d" ,&N ,&M) ;
          for(i = 1 ; i <= N ; i++)
             for(j = 1 ; j <= M ; j++)
                 scanf("%d" ,&money[i][j]) ;
          memset(Bao_Id , -1 , sizeof(Bao_Id)) ;
          scanf("%d" ,&K) ;
          for(i = 1 ; i <= K ; i++){
               scanf("%d%d" ,&Bao[i].x ,&Bao[i].y) ;
               Bao[i].x++ ;
               Bao[i].y++ ;
               Bao_Id[Bao[i].x][Bao[i].y] = i ;
          }
          if(!K){
               puts("0") ;
               continue  ;
          }
          for(i = 0 ; i <= K ; i++)
              for(j = 0 ; j <= K ; j++)
                  dist[i][j] = inf  ;
          for(i = 1 ; i <= K ; i++)
              bfs(Bao[i] , i) ;
          int ans = TSP(K) ;
          if(ans == inf)
               puts("0") ;
          else
               printf("%d\n" ,ans) ;
     }
     return 0 ;
}


POJ 3311

http://poj.org/problem?id=3311

Floyd + TSP

题意是有N个城市(1~N)和一个PIZZA店(0),要求一条回路,从0出发,又回到0,而且距离最短。

const int Max_N = 12 ;
const int inf = (1<<30) ;

int dist[Max_N][Max_N] ;
int N ;
int dp[1<<Max_N][Max_N] ;

void  Floyd(){
      int i , j , k ;
      for(i = 0 ; i <= N  ; i++){
         for(j = 0 ; j <= N ; j++){
              for(k = 0 ; k <= N ; k++){
                   if(dist[i][j] >  dist[i][k] + dist[k][j])
                        dist[i][j] = dist[i][k] + dist[k][j] ;
              }
         }
      }
}

int  DP(){
     int i , j , k ;
     int limit = 1<<(N+1) ;
     for(i = 0 ; i < limit ; i++){
         for(j = 0 ; j <= N ; j++)
            dp[i][j] = inf ;
     }
     for(i = 0 ; i <= N ; i++)
        dp[1<<i][i] = dist[0][i] ;
     for(i = 0 ; i < limit ; i++){
          for(j = 0 ; j <= N ; j++){
              if(i & (1<<j) == 0)  continue ;
              for(k = 0 ; k <= N ; k++){
                 if(i & (1<<k) == 0) continue  ;
                 if(dist[k][j] == inf) continue ;
                 dp[i][j] = min(dp[i][j] , dp[i^(1<<j)][k] + dist[k][j]) ;
              }
          }
     }
     return dp[limit-1][0] ;
}

int  main(){
     int i  , j ;
     while(cin>>N && N){
          for(i = 0 ; i <= N ; i++){
              for(j = 0 ; j <= N ; j++)
                  scanf("%d" ,&dist[i][j]) ;
          }
          Floyd() ;
          printf("%d\n" , DP()) ;
     }
     return 0 ;
}


HDU  4248

http://acm.hdu.edu.cn/showproblem.php?pid=4284


题目:给出一些城市,从1出发,旅游一圈回到1,由于花费可能不够,所以选择一些城市打工,打工之前需要花费d买一个证,工资为c。选中的城市必须去工作一次,而且只能工作一次,问能不能完成旅行.

1:重边:只要取最小的距离就可以
2:从u->v必须要有足够的钱
3:选中的城市必须工作
4:工作前必须先买证才能工作,钱不够不能工作(既不能合并工资和买证而得到受益)


const int Max_N = 108 ;
const int Max_H = 16 ;
const int inf = (1<<29) ;

int  money ;
int  N ;
int  dist[Max_N][Max_N] ;
int  dp[1<<Max_H][Max_H] ;

struct Node{
       int id ;
       int lisence ;
       int makemony ;
       void read(){
            scanf("%d%d%d" ,&id , &makemony , &lisence) ;
       }
}h[Max_H];
int  H ;

void Floyd(){   /*Floyd最精确模板*/
    int i,j,k;
    for(k = 1; k <= N ; k++){
        for(i = 1; i <= N ; i++){
            if(dist[i][k]  == inf)  continue ;
            for(j = 1 ; j <= N ; j++){
                if(dist[k][j] == inf)  continue ;
                dist[i][j] = min(dist[i][j] , dist[i][k] + dist[k][j]);
            }
        }
    }
}

int DP(){
     int i , j , k  , u , v  ;
     int limit = (1<<H) ;
     memset(dp , -1 , sizeof(dp)) ;
     for(i = 0 ; i < H ; i++){
          v = h[i].id ;
          if(money >= dist[1][v] + h[i].lisence)
             dp[1<<i][i] = money - dist[1][v] - h[i].lisence + h[i].makemony ;
     }

     for(i = 1 ; i < limit ; i++){
         for(j = 0 ; j < H ; j++){
              if(dp[i][j] == -1)  continue ;
              if(i & (1<<j) ==0)  continue ;
              u = h[j].id ;
              for(k =0 ; k < H ; k++){
                   if(i & (1<<k))  continue  ;
                   v = h[k].id ;
                   if(dp[i][j] >= dist[u][v] + h[k].lisence){
                       int now = (i ^ (1<<k)) ;
                       dp[now][k] = max(dp[now][k] , dp[i][j] - dist[u][v] - h[k].lisence + h[k].makemony) ;
                   }
              }
         }
     }

     for(i = 0 ; i < H ; i++){
          if(dp[limit-1][i] >= dist[h[i].id][1])
              return 1 ;
     }
     return 0 ;
}

int  main(){
     int T , m , i , j , u , v , w;
     scanf("%d" ,&T) ;
     while(T--){
          scanf("%d%d%d" ,&N ,&m ,&money) ;
          for(i = 1 ; i <= N ; i++){
              for(j = 1 ;  j<= N ; j++)
                   dist[i][j] = (i==j? 0 : inf) ;
          }
          while(m--){
                scanf("%d%d%d" ,&u , &v ,&w) ;
                dist[u][v] = dist[v][u] = min(dist[u][v] , w) ;
          }
          cin>>H ;
          for(i = 0 ; i < H ; i++)
               h[i].read() ;

          Floyd() ;

          if(DP())
             printf("YES\n") ;
          else
             printf("NO\n") ;
     }
     return 0 ;
}


FZU 2120

http://acm.fzu.edu.cn/problem.php?pid=2120

S得到了一个数,他认为相邻位上的数字与数字之间会产生不良影响,比如123,1和2之间产生一个不良影响值,2和3之间产生一个不良影响值。现在他想调整这个数每位的数字的顺序,使得最终得到的数的总的不良影响值最小,且没有前导0。

输入数据的第一行为T表示有T组数据。每组数据先输入一个整数n(0<n<1000000000),接下来输入10*10的矩阵,Aij表示数字i与数字j相邻产生的不良影响值,0<Aij<1000000,矩阵是对称的,Aij与Aji相等。


const int inf = 1<<29 ;

string str ;
int  grid[10][10] ;
int  N ;
int  dp[1<<12][12] ;
int  dist[12][12] ;

int  DP(){
     int i , j , k , limit  , ans = inf ;
     limit = (1<<N) ;
     for(i = 0 ; i < limit ; i++){
         for(j = 0 ; j < N ; j++)
             dp[i][j] = inf ;
     }
     for(i = 0 ; i < N ; i++){
         if(str[i] == '0')
            continue ;
         dp[1<<i][i] = 0 ;
     }
     for(i = 0 ; i < limit ; i++){
         for(j = 0 ; j < N ; j++){
               if(i & (1<<j) == 0)  continue  ;
               for(k = 0 ; k < N ; k++){
                     if(i & (1<<k) == 0)  continue ;
                     dp[i][j] = min(dp[i][j] , dp[i^(1<<j)][k] + dist[k][j]) ;
               }
         }
     }

     for(i = 0 ; i < N ; i++){
          ans = min(ans , dp[limit-1][i]) ;
     }
     return ans ;
}

int  main(){
     int T  , i , j  ;
     cin>>T   ;
     while(T--){
          cin>>str ;
          for(i = 0 ; i <= 9 ; i++){
              for(j = 0  ; j <= 9 ; j++)
                   scanf("%d" ,&grid[i][j]) ;
          }
          N = str.length() ;
          for(i = 0 ; i < N ; i++){
             for(j = 0 ; j < N ; j++)
                  dist[i][j] = grid[str[i]-'0'][str[j]-'0'] ;
          }
          printf("%d\n" ,DP()) ;
     }
     return 0 ;
}




  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值