Ural(Timus) 1119. Metro

DP(网格DP+空间压缩)

题意:给你一个网格,最左下角下标为(0,0),最右上角坐标(N,M),每个格子都是100*100的正方形,从当前点可以向上下左右4个方向移动,如果恰好当前点有对角线,还可以向对角线移动。另外给你K个坐标,表示那些方格有对角线,这些坐标是方格的右上角的坐标,好像(50,50),其实就是(49,49)和(50,50)有斜线连接,(34,67)其实就是(33,66)和(34,67)有连接。现在问你从(0,0)到(N,M)的最短距离,结果四舍五入为整数

 

DP思想还是很简单的,但是要压缩空间否则会MLE。先说说一开始的思路

1.首先,从当前出发,不可能向左或者向下或者向左下走,只可能向上,向右或者向右上走,所以设dp[i][j],表示在坐标(i,j)到(N,M)的最短路。那么初始化dp[N][M]=0;其余点均为INF,我们要的状态是dp[0][0]。然后用记忆化搜索,实现这样需要一个1000*1000的dp数组.

2.回顾上面说的,不可能向左,下,左下走,其实是什么,只能在当前这一行走,或者走向下一行。所以其实我们只需要知道一行的信息——知道了前一行每个点的最短路估计值,用他们来更新这一行的最短路估计值。这样我们就可以采用递推的方式来实现

用一个dp[2][1000]的数组,dp[1][j]表示(0,0)到当前这一行,列为j的点的最短路,dp[0][j]表示当前这一行的前一行。可见“当前行”和“前一行”是一个相对的概念,在不断替换着。

那么状态转移方程也是很容易写出的

dp[1][j]=min{ dp[1][j-1]+100  ,   dp[0][j]+100  ,  dp[0][j-1]+sqrt(20000.0) }

详细看代码

 

#include <cstdio>

#include <cstring>

#include <cmath>

#define inf 100000000.000

#define MAX 1010

double dp[2][MAX];

bool node[MAX][MAX];

int N,M,K;

const double D=sqrt(1.*100*100+1.*100*100);



void DP()

{

    for(int j=0; j<=M; j++)

        dp[0][j]=dp[1][j]=inf;

    dp[1][0]=0;



    int c=0;

    while(c<=N)

    {

        for(int j=0; j<=M; j++)

        {

            if(j-1>=0 && dp[1][j-1]+100 < dp[1][j])

                dp[1][j]=dp[1][j-1]+100;

            

            if(dp[0][j]+100 < dp[1][j])

                dp[1][j]=dp[0][j]+100;

            

            if(node[c][j] && j-1>=0 && dp[0][j-1]+D < dp[1][j])

                dp[1][j]=dp[0][j-1]+D;



        }



        for(int j=0; j<=M; j++)

        {

            dp[0][j]=dp[1][j];

            dp[1][j]=inf;

        }

        c++;

    }



    printf("%.0f\n",dp[0][M]);



    return ;

}

int main()

{

    while(scanf("%d%d",&N,&M)!=EOF)

    {

        scanf("%d",&K);

        int x,y;

        memset(node,0,sizeof(node));

        for(int i=0; i<K; i++)

        {

            scanf("%d%d",&x,&y);

            node[x][y]=1;

        }

        DP();

    }

    return 0;

}

 

 

 

你可能感兴趣的:(metro)