1. 程式人生 > >HDU3001——Travelling(狀態壓縮DP)

HDU3001——Travelling(狀態壓縮DP)

Travelling Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2393    Accepted Submission(s): 692 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

 
Source
2009 Multi-University Training Contest 11 - Host by HRBEU
 

解析:

         TSP問題,類似於我寫的上一篇POJ3311。。。

         只是這題要用三進位制。。。0表示還沒走,1表示走了一次,2表示走了2次

         具體解析見程式碼   

程式碼:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define inf 0x1f1f1f1f

int n,m,map[12][12];
int pos[12]={0,1,3,9,27,81,243,729,2187,6561,19683,59049};
int wei[60000][12]; //wei[s][i]表示狀態s的第i位
int dp[12][60000]; //dp[i][s]表示i號點,狀態為s

void read()
{
    freopen("hdu3001.in","r",stdin);
    freopen("hdu3001.out","w",stdout);
}

void work()
{
    for(int i=0;i<59050;++i) //預處理wei陣列
    {
        int t=i;
        for(int j=1;j<=10;++j)
        {
            wei[i][j]=t%3;
            t/=3;
            if(t==0)break;
        }
    }
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(map,inf,sizeof(map));
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            map[a][b]=map[b][a]=min(map[a][b],c);//處理重邊
        }
        memset(dp,inf,sizeof(dp));
		for(int i=1;i<=n;i++)dp[i][pos[i]]=0;//遞推邊界
		int ans=inf;
		for(int s=0;s<pos[n+1];s++)
		{
			int vis=1;   //標記所有點是否全部走過
			for(int i=1;i<=n;i++)
			{
				if(wei[s][i]==0)vis=0; //有點沒走過
				if(dp[i][s]==inf)continue;
				for(int j=1;j<=n;j++)                               //列舉的點要滿足:j不等於i
				{                                                   //j沒有走兩次以上
					if(i==j||wei[s][j]>=2||map[i][j]==inf)continue;//i與j之間有邊相連
					int S=s+pos[j];  //找出新狀態(即s+3^j)
					dp[j][S]=min(dp[j][S],dp[i][s]+map[i][j]);//更新dp
				}
			}
            if(vis)   //所有點都走過
				for(int i=1;i<=n;i++)
					ans=min(ans,dp[i][s]);
		}
		if(ans==inf)printf("-1\n");else printf("%d\n",ans);
    }
}

int main()
{
    read();
    work();
    return 0;
}