1. 程式人生 > >hdu3001Travelling (狀態壓縮DP,三進位制)

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 由於本題中一個點最多能夠訪問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);
    }
}