例题9-3 UVA - 1347 Tour 旅行(DP)

大体题意:

给你n 个点 已经按照x坐标排了序,问从左边的点一条线的走到右边的点并且一条线的返回起点,除了起点和终点外其余的点各走一次!

思路:

令dp[i][j]表示1~ max(i,j)都走过了,并且第一个人在i位置,第二个人在j位置,假设i > j。

那么1,2,3,,,,i位置都是走过的,第一个人可以走到i + 1,第二个人不动,  或者第二个人走到i+1,第一个人不动。

那么转移方程可以是:dp[i][j] = min(dist[i+1][i] + dp[i+1][j], dist[i+1][j] + dp[i+1][i]);

第一部分  dist[i+1][i] + dp[i+1][j] 的意思是  第二个人从j 走到 i+1,所以dp[i][j] = j到i+1的距离加上dp[i+1][i],因为始终保持i > j !

第二部分意思是  第一个人从i位置走到了i + 1位置  所以dp[i][j] 等于  i到i+1的距离加上 dp[i+1][j]。

这样最后还有一个边界问题,当i = n-1时,  表示1~n-1全部走过了 ,只剩下最后一段路了。所以dp[i][j]等于两个路程相加: dist[n-1][n] + dp[j][n];

因为是i = n-1时情况是知道的!

所以枚举i 要从后往前枚举,j的枚举无所谓了,从前到后 从后到前都可以!!

最后答案就是 dp[2][1] + dist[2][1],画一个图就可以看出  第一个在2位置,第二个在1位置,他们走完后只剩下了1到2这一段路程,加上即可!


详细见代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1000 + 10;
struct Node{
    int x,y;
    void get(int x_,int y_){
        x = x_; y = y_;
    }
    double dis(const Node & rhs){
        return sqrt((rhs.y-y)*(rhs.y-y) + (rhs.x-x) * (rhs.x-x));
    }
}p[maxn];
double dist[maxn][maxn];
double dp[maxn][maxn];
int main(){
    int n;
    while(scanf("%d",&n) == 1){
        memset(dp,0,sizeof dp);
        for (int i = 1; i <= n; ++i){
            int x,y;
            scanf("%d%d",&x,&y);
            p[i].get(x,y);
        }
        for (int i = 1; i <= n; ++i){
            for (int j = 1; j <= n; ++j){
                dist[i][j] = p[i].dis(p[j]);
            }
        }
        for (int i = n-1; i >= 1; --i){
            for (int j = i-1; j >= 1; --j){
                if (i == n-1)dp[i][j] = dist[n-1][n] + dist[j][n];
                else dp[i][j] = min(dist[i+1][j] + dp[i+1][i] , dist[i+1][i] +  dp[i+1][j]);
            }
        }
        printf("%.2lf\n",dp[2][1] + dist[2][1]);

    }
    return 0;
}


=================

复习的代码:

2016.9.28

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1000 + 7;
struct Node{
    double x,y;
    void read(){
        scanf("%lf %lf",&x, &y);
    }
}p[maxn];
double dp[maxn][maxn];
double dist[maxn][maxn];
int main(){
    int n;
    while(scanf("%d",&n) == 1){
        for (int i = 1; i <= n; ++i){
            p[i].read();
        }
        for (int i = 1; i <= n; ++i){
            dist[i][i] = 0;
            for (int j = i+1; j <= n; ++j){
                dist[i][j] = dist[j][i] = sqrt((p[j].x-p[i].x) * (p[j].x-p[i].x) + (p[j].y-p[i].y) * (p[j].y-p[i].y));
            }
        }
        for (int i = 1; i < n-1; ++i){
            dp[n-1][i] = dist[n-1][n] + dist[i][n];
        }
        for (int i = n-2;i >= 1; --i){
            for (int j = i-1; j >= 1; --j){
                dp[i][j] = min(dp[i+1][i] + dist[j][i+1],dp[i+1][j] + dist[i][i+1]);
            }
        }
        printf("%.2f\n",dp[2][1] + dist[1][2]);
    }


    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值