HDU 3001 Travelling 三进制状压DP

题目描述:

Description

After coding so many days,Mr Acmer wants to have a good rest.So travelling is the best choice!He has decided to visit n cities(he insists on seeing all the cities!And he does not mind which city being his start station because superman can bring him to any city at first but only once.), and of course there are m roads here,following a fee as usual.But Mr Acmer gets bored so easily that he doesn’t want to visit a city more than twice!And he is so mean that he wants to minimize the total fee!He is lazy you see.So he turns to you for help.

Input

There are several test cases,the first line is two intergers n(1<=n<=10) and m,which means he needs to visit n cities and there are m roads he can choose,then m lines follow,each line will include three intergers a,b and c(1<=a,b<=n),means there is a road between a and b and the cost is of course c.Input to the End Of File.

Output

Output the minimum fee that he should pay,or -1 if he can’t find such a route.

Sample Input

2 1
1 2 100
3 2
1 2 40
2 3 50
3 3
1 2 3
1 3 4
2 3 10 

Sample Output

100
90
7 

题目分析:

题目大意是从n点中任意一点出发,到其它点至少一次但不能超过两次的最短路径。其中m表示m条路,其中路是双向连通的。
这道题有一个不一样的地方就使得这道题的一个重要变化。一般的状态压缩只有两种情况:0和1,取还是不取,即二进制表示状态压缩之。而在这道题中,我们需要记录的状态是用三进制,原因就是我们需要记录这个点的经过次数是0,1,还是2。既然是三进制,就不能用以往的位运算了,其实除了这个改变的话,和其它的TSP问题差别不太大。

代码如下:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <stdlib.h>

using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;

int state[12]= {0,1,3,9,27,81,243,729,2187,6561,19683,59049}; //几个直达状态
int mp[12][12];
int dp[12][60000];//3^10<60000
//3进制表示,dp[i][j]表示到i点的j状态 每个点状态有0(没走过)1(走了一次)2(走了两次)
int visit[12][60000];
int m,n;

void init()//visit[i][j]表示j状态到达i点几次
{
    memset(visit,0,sizeof(visit));
    for(int i=0; i<60000; i++)
    {
        int t=i;
        for(int j=1; j<=10; j++)
        {
            visit[j][i]=t%3;
            t/=3;
            if (!t) break;
        }//类似与二进制状态的初始化,只不过这里不能用位运算
    }
}

void DP()
{
    int ans=INF;
    for(int i=0; i<=n; i++) dp[i][state[i]]=0;
    for(int i=0; i<state[n+1]; i++)//所有点仅经过这个点且只经过一次的
    {
        bool f=1;
        for(int j=1 ; j<=n; j++)
        {
            if (!visit[j][i]) f=0;
            if (dp[j][i]==INF) continue;
            for(int k=1; k<=n; k++)
            {
                if (k==j || mp[j][k]==INF || visit[k][i]>=2) continue;
                dp[k][i+state[k]]=min(dp[j][i]+mp[j][k],dp[k][i+state[k]]);
                //k行(state[k]表示只经过k状态与i状态(原状态)之和)
                //表示j点i状态通过的方法更新至k点[i+state[k]]状态的方法
            }
        }
        if (f)
        {
            for(int j=1; j<=n; j++)
            {
                ans=min(ans,dp[j][i]);//更新最小值
            }
        }
    }
    if (ans==INF) ans=-1;//无法完成
    printf("%d\n",ans);
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        init();
        memset(dp,INF,sizeof(dp));
        memset(mp,INF,sizeof(mp));
        for(int i=0; i<m; i++)
        {
            int u,v,num;
            scanf("%d%d%d",&u,&v,&num);
            mp[u][v]=mp[v][u]=min(mp[u][v],num);
        }
        DP();
    }
    return 0;
}

你可能感兴趣的:(dp,TSP问题)