uva1347 tour

题目描述:———————————————————————————-这里写图片描述
“从左到右再回来”不太方便思考,可以改成:两个人同时从最左点出发,沿着两条不同
的路径走,最后都走到最右点,且除了起点和终点外其余每个点恰好被一个人经过。这样,
就可以用d(i,j)表示第一个人走到i,第二个人走到j,还需要走多长的距离。
状态如何转移呢?仔细思考后会发现:好像很难保证两个人不会走到相同的点。例如,
计算状态d(i,j)时,能不能让i走到i+1呢?不知道,因为从状态里看不出来i+1有没有被j走
过。换句话说,状态定义得不好,导致转移困难。
下面修改一下:d(i,j)表示1~max(i,j)全部走过,且两个人的当前位置分别是i和j,还需
要走多长的距离。不难发现d(i,j)=d(j,i),因此从现在开始规定在状态中i>j。这样,不管是
哪个人,下一步只能走到i+ 1 , i+2,…这些点。可是,如果走到i+2,情况变成了“1~i和i+
2,但是i+1没走过”,无法表示成状态!怎么办?禁止这样的决策!也就是说,只允许其中
一个人走到i+1,而不能走到i+2, i+3,…。换句话说,状态d(i,j)只能转移到d(i+1,j)和d(i+
1,i) (4) 。
可是这样做产生了一个问题:上述“霸道”的规定是否可能导致漏解呢?不会。因为如果
第一个人直接走到了i+2,那么它再也无法走到i+1了,只能靠第二个人走到i+1。既然如
此,现在就让第二个人走到i+1,并不会丢失解。
边界是d(n-1,j)=dist(n-1,n)+dist(j,n),其中dist(a,b)表示点a和b之间的距离。因为根据
定义,所有点都走过了,两个人只需直接走到终点。所求结果是dist(1,2)+d(2,1),因为第一
步一定是某个人走到了第二个点,根据定义,这就是d(2,1)。
状态总数有O(n 2 )个,每个状态的决策只有两个,因此总时间复杂度为O(n 2 )。

#include<iostream>
#include<math.h>
#include<string.h>
#include<sstream>
#include<iomanip>

#define maxn  1000+10
using namespace std;
int n;
struct node
{
    int x,y;
}loc[maxn];
double dp[maxn][maxn];
double dist[maxn][maxn];

void initial()
{   
    for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
    {
        double detx=(double)(loc[i].x-loc[j].x);
        double dety=(double)(loc[i].y-loc[j].y);
        double temp=detx*detx+dety*dety;
        dist[i][j]=dist[j][i]=sqrt(temp);
    }   
}

double fun(int i,int j)
{   
    double& ans=dp[i][j];
    if(ans>0)return ans;
    if(i==n-1)return ans=dist[i][n]+dist[j][n];

    double p1=fun(i+1,j)+dist[i][i+1];
    double p2=fun(i+1,i)+dist[j][i+1];              
    ans=min(p1,p2);  
    return ans;
}

void function(int n)
{

    for(int i=1;i<=n;i++)
    {
        cin>>loc[i].x>>loc[i].y;
    }
    initial();
    ostringstream oss;
    oss<<setiosflags(ios::fixed)<<setprecision(2)<<fun(2,1)+dist[1][2]<<endl;
    string s=oss.str();
    cout<<s;
    memset(dist,0,sizeof(dist));
    memset(dp,0,sizeof(dp));

}

int main()
{

    while(scanf("%d",&n)!=EOF)
    {
        function(n);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值