BUPT Summer Journey #test7 D

 

 

时间限制 1500 ms   内存限制 65536 KB

题目描述

小叮当刚刚学会了传送门的使用方法,可是它不小心跌落到二维空间一个 n * m 的矩阵格子世界的入口(1,1)处,
他得知出口在(n,m)处,每穿越一个格子门,它的体力值会下降。
又饿又累的他 IQ 已经降为负数了,聪明的你,能帮他规划一下路线,使得它体力值下降的最少吗?
每一行有且仅有一个传送门,但是小叮当上课睡着了,只学会了用传送门瞬移到相邻行的另一个传送门且耗 1 滴体力。
此外,他就只能通过格子门走到相邻四个格子中的一个,也耗 1 滴体力。
ps,由于符合的路径太多了,你只需要告诉我们体力值消耗最小值。

 

输入格式

每组数据,第一行给 n,m 两个整数(2 <= n,m <= 10000),接下来一行,有 n 个数字,代表该行传送门的位置 x( 1 <= x <= m )。以 n,m 都为0结束。Mays温馨提醒:数据组数略大于100。

输出格式

对每组输入,输出一行,体力消耗最小值。

输入样例

5 5
5 4 3 2 1
0 0

输出样例

8
思路:其实,对于一开始的格子,最少走n*m步就可以到达了。然后其实你只要枚举你每次要不要直接走到终点或者到达下一个口,则就是(1,1)向各个传送门建一条权值位曼哈顿距离的边,以及终点建一个曼哈顿距离的边,对于每个传送门都建一条曼哈顿距离的到终点的边和到下一个传送门的权值为1的边。再一边SPFA即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#define M 20000
#define maxn 20005
//#define LOCAL
using namespace std;
struct Point
{
    int x,y;
}door[maxn];
struct Edge
{
    int from,to,next,v;
}e[4*maxn];
int n,m,cnt;
int h[maxn],q[2*maxn],vis[maxn],dist[maxn];
int getdis(int i,int j)
{
    return fabs(door[i].x-door[j].x)+fabs(door[i].y-door[j].y);
}
void Addedge(int x,int y,int v)
{
    cnt++;e[cnt].from=x;e[cnt].to=y;e[cnt].v=v;e[cnt].next=h[x];h[x]=cnt;
}
void Init()
{
    for(int i=1;i<=n+1;i++){Addedge(0,i,getdis(0,i));Addedge(i,0,getdis(0,i));}
    for(int i=1;i<=n-1;i++)
    {
        Addedge(i,i+1,1);
        Addedge(i,n+1,getdis(i,n+1));
        Addedge(i+1,i,1);
        Addedge(n,i,getdis(i,n+1));
    }
    Addedge(n,n+1,getdis(n,n+1));
    Addedge(n+1,n,getdis(n,n+1));
}
void spfa()
{
    for(int i=0;i<=n+1;i++)dist[i]=2000000000;
    int S=0;
    dist[S]=0;int head=0,tail=0;
    q[++tail]=S;vis[S]=1;
    while(head<tail)
    {
        head++;
        int u=q[head];
        for(int i=h[u];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(dist[u]+e[i].v<dist[v])
            {
                dist[v]=dist[u]+e[i].v;
                if(!vis[v])
                {
                    vis[v]=1;tail++;q[tail]=v;
                }
            }
        }
        vis[u]=0;
    }
 
}
int main()
{
    #ifdef LOCAL
    freopen("input.txt","r",stdin);
    #endif // LOCAL
    while(scanf("%d%d",&n,&m)==2,n&&m)
    {
        cnt=0;
        memset(h,-1,sizeof(h));
        memset(vis,0,sizeof(vis));
        door[0].x=1;door[0].y=1;
        for(int i=1;i<=n;i++){door[i].x=i;scanf("%d",&door[i].y);}
        door[n+1].x=n;door[n+1].y=m;
        Init();
        spfa();
        printf("%d\n",dist[n+1]);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值