UVa1347旅行

题意:n个点,坐标给出,设计一条路线,从最左边的点,走到最右边的点,再回来,除最左边的点和最右边的点外,每个点有且经过一次。求最短距离。
分析:
转换一下思路,不是一个人在走,而是两个人从同一起点出发,走不同的两条路,在终点相遇。假设两个人是 i, j,这样可以定义状态 d[i][j] :一个在 i ,一个在 j ,离终点 n 还要多少距离。
但是有一个问题,很难知道下一个要走的点是否被另一个人走过,需要修改状态的定义。
修改一下:d(i,j):1 到max(i,j) 都已经走过了,且两人现在的位置是 i , j。可以看到 d(i,j)=d(j,i)。只是人换了一下而已。既然这样,就规定一下 i > j,这样可以不重复。那么对于当前状态d(i,j),下一个要走的点只能 > i(i+1, i+2…因为i 以下的都走过了)。比如现在A在 i 位置, B 在 j 位置,下一个可能的状态有两个:
A走一步到 i+1, B不动。 d[ i+1 ][ j ] + dis( i , i+1 )
A不动,B走两步到 i+1。 d[ i ][ i+1 ] + dis( j, i+1 )
由于上面规定 i > j,所以第二个状态可以改为 d[ i+1 ][ i ]。
转移方程:d[ i ][ j ] = min( d[ i+1 ][ j ] + dis( i, i+1 ), d[ i+1 ][ i ] + dis( j, i+1 ) ).

边界条件:
d[ n-1 ][ j ] = dis( n-1, n) + dis( j, n ). ( 1<= j< n-1). 因为A走到n-1位置时,<=n-1的位置都已经走过了,所以在其他位置 j 只能直接走到 n.

记忆化搜索代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 1<<30;
const int N = 1000+5;
struct Point{
	double x,y;
}P[N];
double dis(int a, int b){
	return sqrt((P[a].x-P[b].x)*(P[a].x-P[b].x) + (P[a].y-P[b].y)*(P[a].y-P[b].y));
}
double d[N][N];

double solve(int i, int j){
	if(d[i][j] > 0) 
		return d[i][j];
	else 
		return d[i][j] = min(solve(i+1,j) + dis(i,i+1), solve(i+1,i)+dis(j,i+1));
}

int main()
{
	int n; 
	//freopen("in.txt","r",stdin);
	while(scanf("%d",&n) == 1&&n){
		for(int i = 1; i <= n; ++i) scanf("%lf %lf",&P[i].x,&P[i].y);
			
		// 初始化 
		memset(d,0,sizeof(d));
		for(int i = 1; i < n-1; ++i) d[n-1][i] = dis(n-1,n) + dis(i,n); 
		// 规划 
		solve(2,1);
		printf("%.2lf\n",d[2][1]+dis(1,2));
	}
	//fclose(stdin);
	return 0;
}

递推:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 1000+5;
struct Point{
	double x,y;
}P[N];
double dis(int a, int b){
	return sqrt((P[a].x-P[b].x)*(P[a].x-P[b].x) + (P[a].y-P[b].y)*(P[a].y-P[b].y));
}
double d[N][N];

int main()
{
	int n; 
	//freopen("in.txt","r",stdin);
	while(scanf("%d",&n) == 1&&n){
		for(int i = 1; i <= n; ++i) scanf("%lf %lf",&P[i].x,&P[i].y);
		
		if(n == 1){ printf("%.2lf\n",0); continue; }
		if(n == 2){ printf("%.2lf\n",dis(1,2)); continue; }
			
		// 初始化 
		for(int i = 1; i < n-1; ++i) d[n-1][i] = dis(n-1,n) + dis(i,n); 
		
		// 规划 
		for(int i = n-2; i >= 2; --i){
			for(int j = 1; j < i; ++j){
				d[i][j] = min(d[i+1][i]+dis(j,i+1), d[i+1][j]+dis(i,i+1));
			}
		}
		printf("%.2lf\n",d[2][1]+dis(1,2));
	}
	//fclose(stdin);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/CY05627/article/details/88317581
今日推荐