1. 程式人生 > >3進位制狀態壓縮DP——HDU3001 Travelling 旅行商問題

3進位制狀態壓縮DP——HDU3001 Travelling 旅行商問題

題目大意:n個城市之間有m條路,經過每條路徑都有一定的花費,當然有些城市是不可達的。

遍歷所有的城市,並且每個城市最多隻能遍歷2次,求取花費的最小費用並輸出,如果找不到這樣的路徑,輸出-1。(n<=10)

解題思路:由於n是小於10的,所以這個題目可以使用狀態壓縮來做。由於每個城市最多可以遍歷2次,所以需要採用3進位制的狀態壓縮。

基本狀態轉移方程:dp[l][k]=min(dp[l][k],dp[i][j]+Map[j][k])

其中dp[i][j]表示當前狀態是i,當前點是j點。

由狀態方程可知,我們只需要遍歷所有的狀態i,以及所有當前點j和所有的下一個點k就可以了。

原始碼:

//最多遍歷的城市只有10,個那麼每一行的狀態是非常小的,所以可以使用狀態壓縮DP

//經典的旅行商問題,每個城市最多走2遍至少走1遍,求最小遍歷費用
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#define INF 0x3f3f3f3f      //不可達狀態
#include<algorithm>
using namespace std;
int n,m;    //n個城市,m條路,最多10個城市
int Map[11][11];
int dp[60000][11];      //狀態為i時並且這個時候正處在j點的時候的最小費用
int Time[60000][11];    //狀態為i時遍歷j點的次數
int s[11];
void init()
{
    int i,j,t;
    s[0]=1;
    for(i=1;i<=n;i++)
    {
        s[i]=s[i-1]*3;  //求出到達每個點狀態的增量
    }
    for(i=0;i<s[n];i++)
    {
        t=i;
        for(j=0;j<n;j++)
        {
            Time[i][j]=t%3;     //求取當狀態為i的時候遍歷j點的次數
            t=t/3;
        }
    }
    return;
}
void DP()
{
    int i,j,t,k,l,ans,flag;
    ans=INF;
    for(i=0;i<s[n];i++)    //列舉每種狀態
    {
        flag=1;
        for(j=0;j<n;j++)    //列舉當前點j
        {
            if(Time[i][j]==0)   {flag=0; continue;}     //狀態i不包含j點
            if(dp[i][j]==INF)   continue;   //說明該狀態是不存在的
            for(k=0;k<n;k++)    //列舉下一個點k
            {
                if(Map[j][k]==INF)  continue;   //兩點不可達
                if(Time[i][k]==2 || j==k)   continue;   //k點已經被遍歷了2次
                l=i+s[k];
                dp[l][k]=min(dp[l][k],dp[i][j]+Map[j][k]);
            }
        }
        if(flag)    //這個條件表示i狀態包含了所有的點
        {
            for(j=0;j<n;j++)
            {
                ans=min(ans,dp[i][j]);
            }
        }
    }
    if(ans==INF)    printf("-1\n");
    else    printf("%d\n",ans);
    return;
}
int main()
{
    int i,j,k,t;
	//freopen("in.txt","r",stdin);
	while(scanf("%d%d",&n,&m)==2)
	{
	    memset(dp,INF,sizeof(dp));
	    memset(Map,INF,sizeof(Map));
	    memset(Time,0,sizeof(Time));
	    while(m--)
	    {
	        scanf("%d%d%d",&i,&j,&k);
	        i--,j--;        //注意:!!城市是從0開始計數
	        Map[i][j]=Map[j][i]=min(k,Map[i][j]);
	    }
	    init();
	    for(i=0;i<n;i++)    //列舉起點
	    {
	        dp[s[i]][i]=0;   //初始化起點的耗費
	    }
	    DP();
	}
	return 0;
}