题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2224
很经典的TSP一类问题了:给出平面上的N个点(1~N),让你从 1 号点出发走到最右端的点 N ,再返回 1 ,要求中途不能重复走某个点而且必须把所有点全走一遍,求最小的总路径。
我们大致可以这样想:把折返问题拆分成两次从 1 到 N 的路径当然还得满足题目要求(中途不能重复走某个点而且必须把所有点全走一遍)。这样我们设状态量dp[ i ][ j ]来表示从 1 到 i 以及从 1 到 j 的最小总路径(满足题目要求),并且设定i < j,因为显然dp[ j ][ j ]=dp[ j -- 1 ][ j ]+dis[ j-1 ][ j ](两点间距离),那么转移方程我们可以想到:①当 i < j - 1时,dp[ i ][ j ]=dp[ i ][ j - 1 ]+dis[ j - 1 ][ j ]很显然由于i < j-1 ,dp[ i ][ j ]只能来源于dp[ i ][ j - 1 ]。②当i = j - 1 时,dp[ j - 1 ][ j ]就可以来源于dp[ k ][ j -1 ] + dis[ k ][ j ](k < j - 1)。
#include "iostream"
#include "cstdio"
#include "cstring"
#include "algorithm"
#include "cmath"
using namespace std;
int N;
struct NODE
{
double x,y;
}p[205];
double dis[205][205],dp[205][205];
void solve()
{
int i,j,k;
dp[1][2]=dis[1][2];
for(j=3;j<=N;j++)
{
for(i=1;i<j-1;i++)
{
dp[i][j]=dp[i][j-1]+dis[j-1][j];
}
dp[j-1][j]=999999999;
for(i=1;i<j-1;i++)
{
dp[j-1][j]=min(dp[j-1][j],dp[i][j-1]+dis[i][j]);
}
}
dp[N][N]=dp[N-1][N]+dis[N-1][N];
}
int main()
{
while(~scanf("%d",&N))
{
int i,j,k;
for(i=1;i<=N;i++) scanf("%lf %lf",&p[i].x,&p[i].y);
for(i=1;i<=N;i++) for(j=i+1;j<=N;j++) dis[i][j]=sqrt((p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y));
solve();
printf("%.2lf\n",dp[N][N]);
}
}