UVA 1347 Tour DP

  题目链接: 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;
}
View Code

  思考: 这尼玛好难想啊, 这状态真的是好难, 可以看出来我对DP理解还是不够深, 再多做题巩固吧

原文地址:https://www.cnblogs.com/FriskyPuppy/p/7270865.html