POJ 1661 Help Jimmy (图论SPFA)

Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%I64d & %I64u


Description
"Help Jimmy" 是在下图所示的场景上完成的游戏。


场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。

Jimmy老鼠在时刻0从高于所有平台的某处开始下落,它的下落速度始终为1米/秒。当Jimmy落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1米/秒。当Jimmy跑到平台的边缘时,开始继续下落。Jimmy每次下落的高度不能超过MAX米,不然就会摔死,游戏也会结束。

设计一个程序,计算Jimmy到底地面时可能的最早时间。
Input
第一行是测试数据的组数t(0 <= t <= 20)。每组测试数据的第一行是四个整数N,X,Y,MAX,用空格分隔。N是平台的数目(不包括地面),X和Y是Jimmy开始下落的位置的横竖坐标,MAX是一次下落的最大高度。接下来的N行每行描述一个平台,包括三个整数,X1[i],X2[i]和H[i]。H[i]表示平台的高度,X1[i]和X2[i]表示平台左右端点的横坐标。1 <= N <= 1000,-20000 <= X, X1[i], X2[i] <= 20000,0 < H[i] < Y <= 20000(i = 1..N)。所有坐标的单位都是米。

Jimmy的大小和平台的厚度均忽略不计。如果Jimmy恰好落在某个平台的边缘,被视为落在平台上。所有的平台均不重叠或相连。测试数据保证问题一定有解。


Output

对输入的每组测试数据,输出一个整数,Jimmy到底地面时可能的最早时间。


Sample Input
1
3 8 17 20
0 10 8
0 10 13

4 14 3


Sample Output

23

这题本来是一道DP题,但是我觉得作为一道求最短路的题来求解更容易。主要难在构图以及各种坑,构完图就是一道简单的SPFA模板题了- -。

注意:这题先开始我没有仔细审题,结果考虑了很多种复杂的情况导致代码又长又乱,然后一直debug未果,后来经过鑫神的提示才发现自己是一个连中文题都看不清的纱布- -。首先,题目中说了H[i] < Y,说明老鼠一定是从一个悬空的高度掉到一个板上再进行移动的,而我先开始分了初始位置是悬空还是已经在一块板上两种情况了。其次,题目上说了,板的高度各不相同,我先开始还考虑了如果两块板的高度相同并且都能到达的情况QAQ

构图:先把板的左右位置以及高度作为一个结构体,把所有板按照高度从高到低进行排序。小鼠的初始位置为节点0,每块板的左右位置分别为一个节点,然后高度为0的地方设置一个节点。在存边的过程中,需要注意的是,小鼠从一块板的左端点到右端点或者从右端点到左端点的时间也要作为边存进来。还有,就算一块板距离地面的高度是小于MAX米,如果它的下面有一块板挡住了,就必须掉到那块板上而不能直接跟地面连接。

坑1:如果两块板都是[50,100]且高度不同,小鼠可以从上面的100掉到下面的100 - -|| 所有判断的时候得加等号了.

坑2:小鼠可以不经过任何板直接从初始位置掉到地面。


//
//  main.cpp
//  I
//
//  Created by zyh on 15/8/15.
//  Copyright (c) 2015年 zyh. All rights reserved.
//

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#include<stdlib.h>
using namespace std;

int d[2010];
bool vis[2010];
struct edge
{
    int to;
    int cost;
    edge() {}
    edge(int a, int b)
    {
        to = a;
        cost = b;
    }
};
vector<edge> maps[2010];

void add_edge(int s,int t,int cost)
{
    maps[s].push_back(edge(t,cost));
}

struct node
{
    int l,r,h;
}w[1005];

bool cmp(node x,node y)
{
    return x.h>y.h;
}

int SPFA(int s,int t)
{
    queue<int> q;
    q.push(s);
    memset(d,0x3f,sizeof(d));
    memset(vis,0,sizeof(vis));
    vis[s]=1;
    d[s]=0;
    while(!q.empty())
    {
        int p=q.front();
        q.pop();
        vis[p]=0;
        for(int i=0;i<maps[p].size();i++)
        {
            edge e=maps[p][i];
            if(d[e.to]>e.cost+d[p])
            {
                d[e.to]=e.cost+d[p];
                if(!vis[e.to])
                {
                    q.push(e.to);
                    vis[e.to]=1;
                }
            }
        }
    }
    return d[t];
}


int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        for(int i=0;i<2010;i++)
            maps[i].clear();
        int n,x,y,imax;
        scanf("%d%d%d%d",&n,&x,&y,&imax);
        int cnt=0;
        w[cnt].l=x;
        w[cnt].r=x;
        w[cnt++].h=y;
        for(int i=0;i<n;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(c<y)
            {
                w[cnt].l=a;
                w[cnt].r=b;
                w[cnt++].h=c;
            }
        }
        sort(w,w+cnt,cmp);
        for(int i=0;i<cnt;i++)
        {
            int flag1=0,flag2=0;
            for(int j=i+1;j<cnt;j++)
            {
                int high=w[i].h-w[j].h;
                if(high > imax) break;
                if(w[i].l>=w[j].l && w[i].l<=w[j].r && flag1==0)      //根据坑1,一定要加等号!
                {
                    flag1=1;                //当flag1=1的时候,说明板的左端点一定不能直接掉到地面。
                    add_edge(2*i+1,2*j+1,high+w[i].l-w[j].l);
                    //printf("%d %d %d\n",2*i+1,2*j+1,high+w[i].l-w[j].l);
                    add_edge(2*i+1,2*j+2,high+w[j].r-w[i].l);
                    //printf("%d %d %d\n",2*i+1,2*j+2,high+w[j].r-w[i].l);
                }
                if(w[i].r>=w[j].l && w[i].r<=w[j].r && flag2==0)
                {
                    flag2=1;                   //当flag2=1时,说明板的右端点一定不能直接掉到地面。
                    add_edge(2*i+2,2*j+1,high+w[i].r-w[j].l);
                    //printf("%d %d %d\n",2*i+2,2*j+1,high+w[i].r-w[j].l);
                    add_edge(2*i+2,2*j+2,high+w[j].r-w[i].r);
                    //printf("%d %d %d\n",2*i+2,2*j+2,high+w[j].r-w[i].r);
                }
            }
            add_edge(2*i+1,2*i+2,w[i].r-w[i].l);
            //printf("%d %d %d\n",2*i+1,2*i+2,w[i].r-w[i].l);
            add_edge(2*i+2,2*i+1,w[i].r-w[i].l);
            //printf("%d %d %d\n",2*i+2,2*i+1,w[i].r-w[i].l);
            if(w[i].h<=imax)
            {
                if(flag1==0)
                {
                    add_edge(2*i+1,2*cnt+1,w[i].h);
                    //printf("%d %d %d\n",2*i+1,2*cnt+1,w[i].h);
                }
                if(flag2==0)
                {
                    add_edge(2*i+2,2*cnt+1,w[i].h);
                    //printf("%d %d %d\n",2*i+2,2*cnt+1,w[i].h);
                }

            }
        }
        printf("%d\n",SPFA(1,2*cnt+1));       //2*cnt+1即为高度为0处的那个点
    }
    return 0;
}


其实这题并不是很复杂,但是我还是WA了5发,虽然有坑是一个方面,但是我认为主要还是由于我审题不清QAQ,另外膜拜辣些会写DP的巨巨们,蒟蒻表示完全找不准状态和转移QAQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值