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;
}

 

 

 

posted @ 2013-01-20 15:30  Titanium  阅读(547)  评论(0编辑  收藏  举报