hdu 5418 题解

第一眼看到这题,哇,这不是我刚做完的题吗?大水题!然后



这题表面很水,实际上有点坑。

题意

求经过 $ 1 - n $(不能遗漏) 并且回到 $ 1 $ 的最短路。

在看这题之前我们可以来看下这题

最短Hamilton路

这道题的要求是我们要让每个点不重不漏的经过并且最终到达 $ n-1 $ 我们看数据范围,就可以直接状压dp,枚举状态。

由于题目已经给出最短路,便可以直接计算

#include<bits/stdc++.h>
using namespace std;
int n,a[30][30];
int f[1100000][30];
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;++i){
        for(int j=0;j<n;++j){
            scanf("%d",&a[i][j]);
        }
    }
    memset(f,0x3f,sizeof(f));
    f[1][0]=0;
    for(int i=1;i<(1<<n);++i){//状态
        for(int j=0;j<n;++j){//终点
            if((i>>j)&1){
                for(int k=0;k<n;++k){//起点
                    if(((i>>k)&1)&&a[j][k]){//保证有路相通
                        f[i][j]=min(f[i][j],f[i^(1<<j)][k]+a[k][j]);//找没能达到j的
                    }
                }
            }
        }
    }
    printf("%d",f[(1<<n)-1][n-1]);//最后在n-1
    return 0;
}

那么我们回到这题,它给出每个点的边,最后都要经过,并且返回,仔细一想,不就是多了个最短路吗?直接跑一遍 $ floyd $ 枚举终点返回不就好了?

然后我就写下了这个代码

#include<bits/stdc++.h>
using namespace std;
const int INF=1<<30;
int n,m,T,dis[20][20];
int f[(1<<20)][20];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        if(n==1){
            printf("0\n");
            continue;
        }
        for(int i=1;i<=n;++i){
            for(int j=1;j<=n;++j){
                if(i==j) dis[i][j]=0;
                else dis[i][j]=INF;
            }
        }
        for(int i=1;i<=m;++i){
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            dis[x][y]=min(dis[x][y],z);
            dis[y][x]=dis[x][y];
        }
        for(int k=1;k<=n;++k){
            for(int i=1;i<=n;++i){
                for(int j=1;j<=n;++j){
                    if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j];
                }
            }
        }
        memset(f,0x3f,sizeof(f));
        f[1][1]=0;
           for(int i=1;i<(1<<n);++i){
               for(int j=1;j<=n;++j){
                   if((i>>(j-1))&1){
                       for(int k=1;k<=n;++k){
                           if(((i>>(k-1))&1)&&(dis[j][k]!=INF)){
                               f[i][j]=min(f[i][j],f[i^(1<<(j-1))][k]+dis[j][k]);
                           }
                       }
                   }
               }
           }
           int ans=1<<30;
           for(int i=1;i<=n;++i){
               ans=min(ans,f[(1<<n)-1][i]+dis[i][1]);
           }printf("%d\n",ans);
    }
    return 0;
}

然后RE,后面问了乐老师,发现这个题会出现自环

if(n==1){
    printf("0\n");
    continue;
}

这个就是问题的关键所在,不能直接 $ continue $ 因为后面还有边没读进来,会导致RE。

然后我就这样写了一下

#include<bits/stdc++.h>
using namespace std;
const int INF=1<<30;
int n,m,T,dis[20][20];
int f[(1<<20)][20];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        for(int i=0;i<=n;++i){
            for(int j=0;j<=n;++j){
                if(i==j) dis[i][j]=0;
                else dis[i][j]=INF;
            }
        }
        for(int i=1;i<=m;++i){
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            dis[x][y]=min(dis[x][y],z);
            dis[y][x]=dis[x][y];
        }
        if(n==1){
            printf("0");
            continue;
        }
        for(int k=1;k<=n;++k){
            for(int i=1;i<=n;++i){
                for(int j=1;j<=n;++j){
                    if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j];
                }
            }
        }
        memset(f,0x3f,sizeof(f));
        f[1][1]=0;
           for(int i=1;i<(1<<n);++i){
               for(int j=1;j<=n;++j){
                   if((i>>(j-1))&1){
                       for(int k=1;k<=n;++k){
                           if(((i>>(k-1))&1)&&(dis[k][j]!=INF)){
                               f[i][j]=min(f[i][j],f[i^(1<<(j-1))][k]+dis[k][j]);
                           }
                       }
                   }
               }
           }
           int ans=1<<30;
           for(int i=1;i<=n;++i){
               ans=min(ans,f[(1<<n)-1][i]+dis[i][1]);
           }printf("%d\n",ans);
    }
    return 0;
}

听取 $ wa $ 声一片,后面自己看了下题解,觉得思路没问题,看着它把最大值都定义为 $ 0x3f3f3f3f $ 我就一改,然后它过了??

原因是两个最大值不一样导致 $ dp $ 过程中会出现错误,最后我改成了

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
int n,m,T,dis[20][20];
int f[(1<<20)][20];
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        for(int i=0;i<=n;++i){
            for(int j=0;j<=n;++j){
                if(i==j) dis[i][j]=0;
                else dis[i][j]=INF;
            }
        }
        for(int i=1;i<=m;++i){
            int x,y,z;
            scanf("%d %d %d",&x,&y,&z);
            dis[x][y]=min(dis[x][y],z);
            dis[y][x]=dis[x][y];
        }
        if(n==1){
            printf("0\n");
            continue;
        }
        for(int k=1;k<=n;++k){
            for(int i=1;i<=n;++i){
                for(int j=1;j<=n;++j){
                    if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j];
                }
            }
        }
        memset(f,0x3f,sizeof(f));
        f[1][1]=0;
           for(int i=1;i<(1<<n);++i){
               for(int j=1;j<=n;++j){
                   if((i>>(j-1))&1){
                       for(int k=1;k<=n;++k){
                           if(((i>>(k-1))&1)&&(dis[k][j]!=INF)){
                               f[i][j]=min(f[i][j],f[i^(1<<(j-1))][k]+dis[k][j]);
                           }
                       }
                   }
               }
           }
           int ans=INF;
           for(int i=1;i<=n;++i){
               ans=min(ans,f[(1<<n)-1][i]+dis[i][1]);
           }printf("%d\n",ans);
    }
    return 0;
}

然后终于 $ AC $,其实这道题本身不难,主要是在一些细节上的处理,在多组数据下暴露的很明显,平时写题细节方面还是要多多注意。

原文地址:https://www.cnblogs.com/donkey2603089141/p/11736091.html

时间: 2024-10-10 14:07:16

hdu 5418 题解的相关文章

ACM: HDU 5418 Victor and World - Floyd算法+dp状态压缩

HDU 5418 Victor and World Time Limit:2000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u After trying hard for many years, Victor has finally received a pilot license. To have a celebration, he intends to buy himself an airplane and fl

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 Limi

HDU 5418 Victor and World (状态压缩dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5418 题目大意:有n个结点m条边(有边权)组成的一张连通图(n <16, m<100000).求从第一个点出发,经过每个点至少一次后回到原点的最小路径边权和. 分析:发现我还真是菜. n<16,很明显的状态压缩标记,先将所有点的编号减去1,使其从0开始编号.dp[i][j]表示从0号点出发,当前状态为i (二进制位为1表示对应点已走过,否则没走过), 当前位置为 j,  回到原点的最小代价,

hdu - 1237 题解

题意:给出一个数字计算式,包含+,-,*,/四种符号,计算值 题解:最大坑点:不能仅仅判断第一个是0就结束计算,有可能有:0 + 1这样的情况 所以1.判断第一个是否是0,如果是,则判断下一个符号是否是'\n' 2.读入数字和运算符,如果是*,/,取出栈顶元素直接计算完成后压栈,如果是-,将数字相反数压栈,如果是+,将数字压栈. 3.计算栈内元素之和 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm>

2014年北京网络赛 Instrusive HDU 5040 题解 优先队列

网赛的时候看了这道题,发现就是平常的那种基础搜索题. 由于加了一个特殊条件:可以一次消耗3秒或原地停留1秒. 那就不能使用简单的队列了,需要使用优先队列才行. 题意 告诉一副地图:一个起点,一个终点,若干墙,若干监视器,剩下的是空地. 起点,终点,监视器都算空地. 监视器初始值会指定一个方向,共有四个方向. 监视器每秒顺时针转动到下个方向. 监视器视野距离为2. 在监视器的位置或在监视器面向的格子是监视区域. 普通的移动一格需要消耗1秒时间. 在监视器下移动一格需要消耗3秒时间. 如果呆在原地不

hdu - 1231 题解

题意:最大连续子序列问题+输出答案所在的区间 题解:最大连续子序列问题状态转移方程:f[i]=max(a[i],f[i-1]+a[i]) 答案所在区间的话可以在递推求状态的时候,顺便记录一下当前位置所在的序列左端点是谁,最后扫描的时候记录下最优解的位置,然后这个位置就是右端点,记录过的数据就是左端点. 1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring&g

hdu 5418 Victor and World (最短哈密顿回路)

给你n个国家,m条路线:u_i与v_i之间的距离w_i. 输出从1号国家出发经过每个国家至少一次再回到1号国家的最短距离. [官方题解]: 我们首先需要预处理出任意两个国家之间的最短距离,因为数据范围很小,所以直接用Floyd就行了. 之后,我们用f[S][i]表示访问国家的情况为S,当前最后访问的一个国家是i所需要的最小总油量,其中,S的二进制表示记录了访问国家的情况,S在二进制表示下的第i位(不管是从左往右还是从右往左都可以).如果是1则表示第i个国家被访问过了,否则表示第i个国家没有被访问

HDU 5573 题解

题意:关于一只要续命的青蛙(雾),一颗完全二叉树,它的标号正如我们所期望的根为1,左儿子为父节点*2,右儿子为父节点*2+1,然后老青蛙从根往下走,一共走K步,它需要n个灵魂,每走过一个点,可以减去或加上这个点的标号,输出一种可能方案,输入数据保证至少有一组解 1=<n<=1e9,N<=2k<=260 共1~100组数据,1000MS 题解/思路:其实这道题找一下规律就好了,你会发现最左边两条路径分别可以表示±1,±3,--,±(2k-1)和±2,±4,--,±2k,所以只需判断这

HDU 5521 题解

题意:给出n个点,John需要和一个住在第n个点的人在某个点碰面,再给出m个集合,每个集合中包含Si个点,这些点两两之间可以以ti时间互达,求两人需花费的最小时间,若无法走到第n个点,则输出Evil John. 2<=N<=100000;1<=ti<=1e9;Si>0;∑Si<=1e6; 共1~6组数据,6000MS 算法/思路: 第一次见到这种套路的话还是蛮有趣的,对每个集合中两两点建边显然会被轻松TLE. 使用最短路+虚拟节点:于是我们对每个集合建立一个虚拟节点,在