hdu3001Travelling (状态压缩DP,三进制)

Travelling

Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3611 Accepted Submission(s): 1132


Problem 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

Source
2009 Multi-University Training Contest 11 - Host by HRBEU 
由于本题中一个点最多能够访问2次,由此可以联想到3进制;
dp[j] [i] 表示在状态i下到达终点j走过的最小的路程。具体下面的代码中有解释。
#include<stdio.h>
int map[12][12],n,dp[12][60000];//dp[i][state]表示state状态以i点结尾的最小路径
int Min(int a,int b)
{
    return a>b?b:a;
}
int toArray(int three[],int sum)
{
    int k=0;
    for(int i=0;i<n;i++)
    {
        three[i]=sum%3; sum/=3; if(three[i])k++;
    }
    return k;//在状态sum中有k个点己经走过
}
int main()
{
    int m,a,b,c,in[12],MIN,three[12],k;
    in[0]=1;
    for(int i=1;i<=10;i++)in[i]=in[i-1]*3; //3的i次方
    while(scanf("%d%d",&n,&m)==2)
    {
        for(int i=0;i<n;i++)//初始化
        {
            for(int j=0;j<in[n];j++)
                dp[i][j]=-1;
            for(int j=0;j<n;j++)
            map[i][j]=-1;
        }
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c); a--; b--;
            if(map[a][b]!=-1)//判重
            map[a][b]=map[b][a]=Min(map[a][b],c);
            else
            map[a][b]=map[b][a]=c;
        }
        MIN=-1;
        for(int Go=1;Go<in[n];Go++)//枚举己走的状态Go
        {
           k=toArray(three,Go);
           for(int i=0;i<n;i++)
           if(three[i])//状态Go以点i结尾时
           {
                if(k==1)//只有一个点
                    dp[i][Go]=0;

                if(dp[i][Go]==-1)continue;//状态Go不能以点i为结尾点
                if(k==n)//n个点己经走过
                {
                    if(MIN==-1)MIN=dp[i][Go];
                    else  MIN=Min(MIN,dp[i][Go]);
                }
                for(int j=0;j<n;j++)//找个点j,从i点走到j点,状态tGo以j点结尾
                if(i!=j&&three[j]<2&&map[i][j]!=-1)
                {
                    int tGo=Go+in[j];
                    if(dp[j][tGo]==-1)
                        dp[j][tGo]=dp[i][Go]+map[i][j];
                    else
                        dp[j][tGo]=Min(dp[i][Go]+map[i][j],dp[j][tGo]);
                }
           }
        }

        printf("%d\n",MIN);
    }
}


你可能感兴趣的:(枚举,压缩,dp)