题目链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=446&page=show_problem&problem=4093
题目描述: 有N个点, 要求求一个回路的最短距离。(N <= 1000)
解题思路: 一开始想暴力的, 因为实在想不出其他方法了啊....走一圈周长最短可以等价为两个人同时从最左端出发,沿着不同的路径走到最右端。如果定义d(i,j)表示1~max(i,j)全部走过,第一个 人在i,第二个人在j,还需要走多长的距离。此时可以规定i>j,这样,还可以规定i,j中只有一个人允许走到i+1这一点。这样的话可以保证不会出现某些点跳过的情况。状态转移方程如下:
d(i,j)=min(d(i+1,j)+dist(i,i+1),d(i+1,i)+dist(j,i+1));
第二项表示为d(i+1,i)是因为已经规定i>j,又根据d(i,j)的定义易知有d(i,j)=d(j,i),因此d(i,i+1)可写为d(i+1,i)。本题的状态数有O(N^2)个,每次状态的决策只有2个,因此时间复杂度为 O(N^2)。本题中由于j<i,因此还可以提前计算好所有j<i的距离,这样可以避免重复计算距离,进一步提升效率。
代码:
#include <iostream> #include <cstdio> #include <string> #include <vector> #include <map> #include <cstring> #include <iterator> #include <cmath> using namespace std; struct Node { double x; double y; Node() { x = y = 0; } Node( double _x, double _y ) { x = _x; y = _y; } }; Node a[1050]; double d[1050][1050]; // d(i, j)表示1 ~ max(i, j)都已经走过, 且第一个人到了i, 第二个人到了还需要走多长距离 int n; double dis( int i, int j ) { double dx = a[i].x - a[j].x; double dy = a[i].y - a[j].y; return sqrt(dx*dx + dy*dy); } double dp( int i, int j ) { double & ans = d[i][j]; if( ans > 0 ) return ans; if( i == n-1 ) return ans = dis(i, n) + dis(n, j); return ans = min( dp(i+1, j) + dis(i, i+1), dp(i+1, i) + dis(i+1, j) ); } int main() { while( ~scanf( "%d", &n ) ) { memset(a, 0, sizeof(a)); memset(d, 0, sizeof(d)); for( int i = 1; i <= n; i++ ) { scanf( "%lf%lf", &a[i].x, &a[i].y ); } printf( "%.2lf ", dp(2, 1)+dis(2, 1) ); } return 0; }
思考: 这尼玛好难想啊, 这状态真的是好难, 可以看出来我对DP理解还是不够深, 再多做题巩固吧