题意
给出坐标系中n个点,要求从最左边的点出发,每一步只能往右走,走到最右边那个点。然后再从最右边那个点出发,每一步只能往左走,回到最左边那个点。整个过程中(除了起点),每个点必须经过且仅经过一次。问最短路(欧几里得距离)?
$n leq 1000$。
Solution
问题等价于两个人从起点出发,走到最右边,两人不能经过同一个点,且两个人经过点的并集为所有点。那么一个人的路径确定以后另个人的路径一定确定。(就是去填补还没走的所有点)
$dp_{i,j}$表示$1..i$已经走完,且另一个人走到了$j$。枚举状态是$O(n^2)$的,可以通过维护路径前缀和$O(1)$转移。
复杂度$O(n^2)$。
记忆化搜索
/*By DennyQi 2019*/ #include <cstdio> #include <queue> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; const int MAXN = 1010; const int MAXM = 20010; const int INF = 0x3f3f3f3f; inline int Max(const int a, const int b){ return (a > b) ? a : b; } inline int Min(const int a, const int b){ return (a < b) ? a : b; } inline int read(){ int x = 0; int w = 1; register char c = getchar(); for(; c ^ '-' && (c < '0' || c > '9'); c = getchar()); if(c == '-') w = -1, c = getchar(); for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w; } struct Coordinate{ double x,y; }a[MAXN]; int n; double dp[MAXN][MAXN]; bool vis[MAXN][MAXN]; inline bool cmp(const Coordinate& a, const Coordinate& b){ return a.x < b.x; } inline double dist(int i, int j){ double res = 0.0; res = sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x) + (a[i].y-a[j].y)*(a[i].y-a[j].y)); return res; } double DP(int a, int b){ if(a < b) swap(a,b); if(a == n){ return dist(b,n); } if(vis[a][b] || vis[b][a]) return dp[a][b]; vis[a][b] = vis[b][a] = 1; dp[a][b] = min(DP(a+1,b)+dist(a,a+1), DP(a,a+1)+dist(b,a+1)); dp[b][a] = dp[a][b]; return dp[a][b]; } int main(){ freopen(".in","r",stdin); n = read(); for(int i = 1; i <= n; ++i){ scanf("%lf%lf",&a[i].x,&a[i].y); } sort(a+1,a+n+1,cmp); printf("%.2f", DP(1,1)); return 0; }
刷表法
dp[1][1] = 0; for(int i = 1; i <= n; ++i){ for(int j = i; j <= n; ++j){ if(i==j && i>1 && i<n) continue; if(j < n){ dp[i][j+1] = min(dp[i][j+1], dp[i][j] + dist(j,j+1)); dp[j][j+1] = min(dp[j][j+1], dp[i][j] + dist(i,j+1)); } else{ dp[n][n] = dp[i][n] + dist(i,n); } } }
填表法
dp[1][1] = 0; for(int i = 1; i <= n; ++i){ for(int j = i; j <= n; ++j){ if(i==j && i<n) continue; dp[i][j] = dp[i][j-1] + dist(j-1,j); if(i+1 == j){ for(int k = i-1; k >= 1; --k){ dp[i][j] = min(dp[i][j], dp[i][k] + dist(k,j)); } } dp[j][i] = dp[i][j]; } }