poj 3001 Travelling (状压DP)

每个顶点最多可访问两次的TSP问题。
对于每个顶点,用一个三进制位表示其访问次数,则n个顶点的状态为n位3进制数。
设dp[i][j]表示在状态i下到达顶点j的最小花费。
有状态转移方程:

dp[i+3j1][j]=min(dp[i+3j1][j],dp[i][k]+dis[k][j])
其中dis[k][j]为顶点k到j的距离。

说明:

  1. 初始化时,将dp初始化为INF(无穷大),dp[3^(i-1)][i]初始化为0,即从顶点i出发回到i的最小花费为0。
  2. 由状态转移方程知,转移是从状态i转移到状态i+3^(j-1)。在枚举状态的时候,需要考虑状态i是否合法,即第j位上的数是否小于2,若大于等于2,此时是不能转移的。
  3. 对于最终的结果,根据状态的定义,取dp[3^n-1][i]的最小值即为答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define maxn 11
typedef __int64 LL;
#define inf 0x3f3f3f3f

int D[maxn][maxn];
int dp[60000][maxn];

int getnum(int s,int k)
{
    int cnt=1;
    while(s){
        int tmp=s%3;
        if(cnt==k) return tmp;
        s/=3;
        ++cnt;
    }
    return 0;
}

int a[11];

int main()
{
    int n,i,j,k,m;
    a[0]=1;
    for(i=1;i<=10;++i) a[i]=3*a[i-1];
    while(~scanf("%d%d",&n,&m))
    {
        memset(D,inf,sizeof(D));
        for(i=1;i<=n;++i) D[i][i]=0;
        for(i=1;i<=m;++i){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            D[x][y]=D[y][x]=min(D[x][y],z);
        }
        memset(dp,inf,sizeof(dp));
        for(i=1;i<=n;++i) dp[a[i-1]][i]=0;
        for(i=1;i<a[n];++i)
        {
            for(j=1;j<=n;++j)
            {
                if(dp[i][j]==inf) continue;
                for(k=1;k<=n;++k)
                {
                    if(D[j][k]==inf||getnum(i,k)>=2) continue;
                    dp[i+a[k-1]][k]=min(dp[i+a[k-1]][k],dp[i][j]+D[j][k]);
                }
            }
        }
        int ans=inf;
        for(i=1;i<=n;++i) ans=min(ans,dp[a[n]-1][i]);
        printf("%d\n",ans==inf?-1:ans);
    }
    return 0;
}

你可能感兴趣的:(poj 3001 Travelling (状压DP))