1. 程式人生 > >【紫書】(UVa1347)Tour

【紫書】(UVa1347)Tour

繼續考慮dp題目。

題意分析

其實這裡只是更加仔細的做一個lrj的復讀機(Orz
他分析了一個很重要的結果:如果是一個人從左到右再回來,並且每個點恰經過一次,那麼等價於兩個人從左到右每個點經過一次地遍歷這些點。因為這樣,我們才能夠得到d[i,j]這樣的方法來記錄值。
但是這樣也不夠。d[i,j]我們最開始得到的是比較簡單的記錄狀態:A走到i,B走到j得到的最小距離——那我怎麼知道A和B會不會重複的走?(i,j)能不能轉移到(i+1,j)?我們的狀態裡面沒有儲存這個結論。也就是說,這個狀態定義的不好。
因此,我們定義成這樣:把原來的狀態表示成1max(i,j)的點全部走過的情況下兩人能夠得到的最小值。那麼狀態的轉移就很顯然了。考慮到d

(i,j)=d(j,i),規定i>j。直接就可以得到d(i,j)=min(d(i+1,j)+dist(i,i+1),d(i+1,i)+dist(j,i+1))
這樣會不會漏解呢?不會。如果i能夠直接走到i+2,那麼根據定義就無法走到i+1了。因此,我們讓j走到i+1,是能夠做到不遺漏的——因為我們之前考慮的情況不存在。
從上面不是我的分析可以看出,一個對題目深入分析得到的狀態對dp題目的解決多麼重要。

程式碼

很神祕,我用記憶化搜尋寫了半天,tle了六次……可能真的是有效率問題。以我現在的水平搞不明白是怎麼回事,以後再解決吧。但是這裡用迴圈不困難。

#include <cstring>
#include <algorithm> #include <cstdio> #include <cstdlib> #include <iostream> #include <cmath> #include <queue> #include <set> #include <iomanip> #include <vector> #define ZERO(x) memset((x),0,sizeof(x)) using namespace std; //const int maxn=; int x[1005
],y[1005]; double dp[1005][1005]; double dist[1005][1005]; double inf; int n; int main() { while(scanf("%d",&n)==1) { memset(dp,0x43,sizeof(dp)); inf=dp[0][0]; for(int i=1;i<=n;++i) scanf("%d%d",&x[i],&y[i]); for(int i=1;i<=n;++i) for(int j=1;j<i;++j) dist[j][i]=dist[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); for(int i=n-1;i>=1;--i) for(int j=1;j<i;++j) if(i==n-1) dp[i][j]=dist[i][n]+dist[j][n]; else dp[i][j]=min(dp[i+1][j]+dist[i][i+1],dp[i+1][i]+dist[i+1][j]); printf("%.2lf\n",dp[2][1]+dist[1][2]); } return 0; }