1. 程式人生 > >JZOJ 3510. 【NOIP2013模擬11.5B組】最短路徑

JZOJ 3510. 【NOIP2013模擬11.5B組】最短路徑

目錄:

題目:

由於每個點要麼在去的路上,要麼在回來的路上,所以用二進位制數表示N個點的狀態,對於特殊的四個點特判一下,然後從所有狀態中取最優的

期望得分:20分
考慮到每個點只能走一次,且從終點往回走和從起點再走一遍到終點沒有區別,所以這道題可以轉化為求兩條不相交路徑和的最小值。
於是考慮用動態規劃求解。
F[i][j]表示第一個點走到i,第二個點(回去的那個點)走到j的最優值。
為了保證更新時不會更新出F[i][i](即一個點走了兩次),而且每個點都會在路徑上,我們每次用

F[i][j]去更新點max(i,j)+1,所以轉移方程為:
F[0][0]=0,k=max(i,j)+1
F[k][j]=max{F[k][j],F[i][j]+Dis(i,k)};
F[i][k]=max{F[i][k],F[i][j]+Dis(j,k)};
Dis(i,j)為從i直接走到j點的距離.
對於兩個特殊點和
max{i,j}=n
的情況特判處理即可。
期望得分:100
同時上面的DP也可以用記憶化搜尋實現,對於abs(xy)>1的情況,說明當前情況只能從max{x,y}1轉移過來,當abs(xy)=1時,則能從1min{x,y}中的任意一點轉移過來,於是用記憶化搜尋完成上面的步驟,加上適當剪枝即可。
期望得分:60~100分

程式碼:

#pragma GCC optimize("3")
#include<iostream>
#include<cstdio>
#include<cstring> #include<algorithm> #include<cmath> #include<queue> #define LL long long using namespace std; inline LL read() { LL d=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();} return d*f; } int x[1001],y[1001]; double f[1001][1001],t[1001][1001]; int main() { /* freopen("path.in","r",stdin); freopen("path.out","w",stdout);*/ int n=read(),b1=read()+1,b2=read()+1; for(int i=1;i<=n;i++) x[i]=read(),y[i]=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(i==j) continue; t[j][i]=t[i][j]=(double)sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=99999999; f[1][1]=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int k=max(i,j)+1; if(k==n+1) { if(i==n) f[n][n]=min(f[n][n],f[i][j]+t[j][n]); else f[n][n]=min(f[n][n],f[i][j]+t[i][n]); continue; } if(k!=b1) f[i][k]=min(f[i][k],f[i][j]+t[j][k]); if(k!=b2) f[k][j]=min(f[k][j],f[i][j]+t[i][k]); } printf("%.2lf",f[n][n]); fclose(stdin); fclose(stdout); return 0; }