1. 程式人生 > >Hie with the Pie

Hie with the Pie

題目 mic font fine 比較 iostream pri poj eof

Hie with the Pie poj-3311

    題目大意:n+1個點,旅行商問題。

    註釋:n<=10。

      想法:咳咳,第一道狀壓dp,下面我來介紹一下狀壓dp。

        所謂dp,就是動態性決策規劃,通過上一時刻或上幾時刻的狀態來更新當前狀態並且無後效性。而狀壓dp就是將之前的狀態通過二進制表現出來。幾個例子。有五個格子_ _ _ _ _。上面可以放棋子或者不放。我們將放棋子的格子標註為1,不放棋子的格子標註為2。那麽,我們就可以用一個二進制數來表達出人任何一個的完整狀態而不是片面的,這就是狀壓dp。但是由於我們需要表示出所有的狀態,所以狀壓dp的空間復雜度是指數級的,這就比較的傷心。所以看見題目的數據範圍有那麽一個小的可憐的,可以考慮考慮狀壓dp。狀壓dp前置知識點是位運算,在此不做介紹。

      關於這道題,我們設dp[s][i]。表示s這個狀態的最小代價,且這個狀態最後到達的點是i。之後轉移就是枚舉s的上一個到達的點。在此,我們要註意,題目中給出的是鄰接矩陣的形式,我們先用floyd求出兩點之間的最短路,之後通過上一個到達的點的狀態加上dis[j][i]來更新當前狀態。

    最後,附上醜陋的代碼... ...

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #define inf 0x3f3f3f3f
 5 using namespace
std; 6 int dp[5000][20]; 7 int map[20][20]; 8 int main() 9 { 10 int n; 11 while(1) 12 { 13 memset(map,0x3f,sizeof(map)); 14 memset(dp,0,sizeof(dp)); 15 scanf("%d",&n); 16 if(!n) return 0; 17 for(int i=0;i<=n;i++) 18 { 19 for
(int j=0;j<=n;j++) 20 { 21 scanf("%d",&map[i][j]); 22 } 23 } 24 for(int k=0;k<=n;k++)//floyd 25 { 26 for(int i=0;i<=n;i++) 27 { 28 for(int j=0;j<=n;j++) 29 { 30 map[i][j]=min(map[i][j],map[i][k]+map[k][j]); 31 } 32 } 33 } 34 for(int S=0;S<=(1<<n)-1;S++)//枚舉所有狀態 35 { 36 for(int i=1;i<=n;i++)//考慮最後到達的點 37 { 38 if(S&(1<<(i-1)))//現判斷i是不是s中已經到達的點 39 { 40 if(S==(1<<(i-1)))//特判,如果s只到達過i 41 { 42 dp[S][i]=map[0][i]; 43 } 44 else 45 { 46 dp[S][i]=inf; 47 for(int j=1;j<=n;j++) 48 { 49 if(S&(1<<(j-1))&&(j!=i)) 50 { 51 dp[S][i]=min(dp[S^(1<<(i-1))][j]+map[j][i],dp[S][i]); 52 } 53 } 54 } 55 } 56 } 57 } 58 int ans=inf; 59 for(int i=1;i<=n;i++)//最後所有的點全部都到達後,枚舉最後到達的點去統計答案。 60 { 61 ans=min(ans,dp[(1<<n)-1][i]+map[i][0]); 62 } 63 printf("%d\n",ans); 64 } 65 }

  小結:題目中明確指出所給的鄰接矩陣可能不是對稱的,容易在floyd時將其按照對稱處理。

Hie with the Pie