HDU 3001 Travelling

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3001

题意:一个人想去N个地方,且每个地方最多去两次,至少去一次。问最短的路径是多少。


分析:状态压缩DP,类似于旅行商问题。但每个地方有3个状态:没去过,去过一次,去过两次。因此可以用3进制记录每个点的3种状态。而且此题只要求到达每个地方即可,并不需要回到出发点。


Code:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <map>
#include <set>
#define LL long long
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;

const int inf=0x3f3f3f3f;
int dp[60000][12],dis[12][12],f[60000][12],fac[12];
int n,m;

void init(){
    fac[1]=1;
    for(int i=2;i<=11;i++)
        fac[i]=fac[i-1]*3;
    for(int i=1;i<59055;i++){
        int t=i;
        for(int j=1;j<=10;j++){
            f[i][j]=t%3;
            t/=3;
        }
    }
}

int main()
{
    init();
    while(scanf("%d %d",&n,&m)==2){
        memset(dis,inf,sizeof(dis));
        int u,v,w;
        while(m--){
            scanf("%d %d %d",&u,&v,&w);
            if(dis[u][v]>w) dis[u][v]=dis[v][u]=w;
        }
        memset(dp,inf,sizeof(dp));
        for(int i=1;i<=n;i++)
            dp[fac[i]][i]=0;
        int ans=inf;
        for(int s=0;s<fac[n+1];s++){//从小到大枚举集合
            bool flag=true;
            for(int i=1;i<=n;i++){//枚举集合中的起点
                if(f[s][i]==0) {flag=false; continue;}//如果i点不在集合中就直接跳过
                if(dp[s][i]==inf) continue;
                for(int j=1;j<=n;j++){//枚举集合外的终点
                    if(i==j||dis[i][j]==inf||f[s][j]>=2) continue;
                    //如果i和j是同一个点,点i和点j不连通,在s状态下j点已经走过2次以上就直接跳过
                    int news=s+fac[j];
                    dp[news][j]=Min(dp[news][j],dp[s][i]+dis[i][j]);
                }
            }
            if(flag) {
                for(int i=1;i<=n;i++)
                    ans=Min(ans,dp[s][i]);
            }
        }
        printf("%d\n",ans==inf?-1:ans);
    }
    return 0;
}



你可能感兴趣的:(HDU 3001 Travelling)