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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值