zoukankan      html  css  js  c++  java
  • HDU 5418 Victor and World 允许多次经过的TSP

    题目链接:

    hdu: 

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

    bestcoder(中文):

      http://bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=619&pid=1002

    Victor and World

     
     Accepts: 99
     
     Submissions: 644
     Time Limit: 4000/2000 MS (Java/Others)
     
     Memory Limit: 262144/131072 K (Java/Others)
    问题描述
    经过多年的努力,Victor终于考到了飞行驾照。为了庆祝这件事,他决定给自己买一架飞机然后环游世界。他会驾驶一架飞机沿着规定的航线飞行。在地球上一共有nn个国家,编号从11nn,各个国家之间通过mm条双向航线连接,第ii条航线连接第u_iui个国家与第v_ivi个国家,通过这条航线需要消耗w_iwi升油,且从11号国家可以直接或间接到达22nn中任意一个国家。
    
    Victor一开始位于11号国家,他想知道从11号国家出发,经过各个国家至少一次并最后回到11号国家消耗的总油量的最小值是多少。
    输入描述
    第一行包含一个整数TT,表示测试数据的组数。
    
    每组测试数据的第一行有两个整数nnmm,表示国家的个数和航线的条数。
    
    接下来mm行,每行三个整数u_iui, v_ivi, w_iwi,描述一条航线。
    
    1leq Tleq 201T201leq nleq 161n161leq mleq 1000001m1000001leq w_ileq 1001wi1001leq u_i, v_i leq n1ui,vin
    输出描述
    每组测试数据输出一行一个整数,即消耗的总油量的最小值。
    输入样例
    1
    3 2
    1 2 2
    1 3 3
    输出样例
    10

    题解:

      这一道题与普通tsp的差别就在与状太转移变多了,设i,j为任意的两个点,s为压缩后的状态。dp[s][i]可以由dp[s][j]转移而来,这直接破坏普通TSP无后效性的前提。因此如果还想要用dp来解这道题,就要做一点改变创新了。

    方法一:比较暴力的状态压缩

      这里状态转移用的是刷表法

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 
     7 const int maxn=(1<<16)+10;
     8 static int INF;
     9 
    10 int dp[maxn][33],cnt[maxn];
    11 
    12 int mat[33][33];
    13 
    14 int n,m;
    15 
    16 void get_cnt(){
    17     memset(cnt,0,sizeof(cnt));
    18     for(int stat=0;stat<(1<<16);stat++){
    19         //统计每一个状态的已经访问了的节点的个数
    20         for(int i=0;i<16;i++){
    21             if(stat&(1<<i)) cnt[stat]++;
    22         }
    23     }
    24 }
    25 
    26 void init(){
    27     memset(dp,0x3f,sizeof(dp));
    28     INF=dp[0][0];
    29     memset(mat,0x3f,sizeof(mat));
    30 }
    31 
    32 int main(){
    33     get_cnt();
    34     int tc;
    35     scanf("%d",&tc);
    36     while(tc--){
    37         init();
    38         scanf("%d%d",&n,&m);
    39         int u,v,w;
    40         while(m--){
    41             scanf("%d%d%d",&u,&v,&w);
    42             u--; v--;
    43             if(w<mat[u][v])
    44                 mat[u][v]=mat[v][u]=w;
    45         }
    46         dp[1][0]=0;
    47         for(int s=1;s<(1<<n);s++){
    48             //做k次后能够保证引起后效性的那些转移也能得到最优解,有点像floyd算法的思想。 
    49             for(int k=0;k<=cnt[s];k++){
    50                 for(int i=0;i<n;i++){
    51                     //这里要做可行性减枝,否则时间会爆。 
    52                     if(dp[s][i]!=INF){
    53                         for(int j=0;j<n;j++){
    54                             dp[s|(1<<j)][j]=min(dp[s|(1<<j)][j],dp[s][i]+mat[i][j]);
    55                         }
    56                     }
    57                 }
    58             }
    59         }
    60         printf("%d
    ",dp[(1<<n)-1][0]);
    61     }
    62     return 0;
    63 }
    View Code

     方法二:先floyd,再直接上普通tsp的做法

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 using namespace std;
     5 
     6 const int maxn = 33;
     7 
     8 int mat[maxn][maxn];
     9 int dp[(1 << 16)+10][maxn];
    10 
    11 int n, m;
    12 
    13 void floyd() {
    14     for (int k = 0; k < n; k++) {
    15         for (int i = 0; i < n; i++) {
    16             for (int j = 0; j < n; j++) {
    17                 mat[i][j] = min(mat[i][j], mat[i][k] + mat[k][j]);
    18             }
    19         }
    20     }
    21 }
    22 
    23 void init() {
    24     memset(mat, 0x3f, sizeof(mat));
    25     for (int i = 0; i < maxn; i++) mat[i][i] = 0;
    26     memset(dp, 0x3f, sizeof(dp));
    27 }
    28 
    29 int main() {
    30     int tc;
    31     scanf("%d", &tc);
    32     while (tc--) {
    33         init();
    34         scanf("%d%d", &n, &m);
    35         int u, v, w;
    36         while (m--) {
    37             scanf("%d%d%d", &u, &v, &w);
    38             u--; v--;
    39             if (mat[u][v] > w) mat[u][v] = mat[v][u] = w;
    40         }
    41         floyd();
    42         dp[1][0] = 0;
    43         //这里只是做出一个哈密顿通路
    44         for (int s = 1; s < (1 << n); s++) {
    45             for (int i = 0; i < n; i++) {
    46                 if (s&(1 << i)) {
    47                     for (int j = 0; j < n; j++) {
    48                         if ((s&(1 << j))== 0) {
    49                             dp[s | (1 << j)][j] = min(dp[s | (1 << j)][j], dp[s][i] + mat[i][j]);
    50                         }
    51                     }
    52                 }
    53             }
    54         }
    55         //补回路
    56         int ans = 0x3f3f3f3f;
    57         for (int i = 1; i < n; i++) {
    58             ans = min(ans, dp[(1 << n) - 1][i] + mat[i][0]);
    59         }
    60         if (n == 1) printf("0
    ");
    61         else printf("%d
    ", ans);
    62     }
    63     return 0;
    64 }
    View Code
  • 相关阅读:
    函数及习题
    数组和集合
    数组和集合实例
    普通集合和泛型集合的区别,哈希表和字典表的区别,队列和堆栈的区别以及堆和栈的区别。
    c#时间表示
    c#正则表达式
    js正则实例
    习题实例
    c#数据类型
    简单控件
  • 原文地址:https://www.cnblogs.com/fenice/p/5293657.html
Copyright © 2011-2022 走看看