HDU3440House Man(差分约束)

题意:有N个在一条直线上的房子, 每个房子有着不同的高度, 一个超人可以将这些房子左右移动但不能改变房子之间的相对位置.现在超人要从最矮的房子跳到刚好比他高的房子上面, 且每次跳的房子都要比当前房子要高.那么最后超人肯定会跳到最高的房子上面, 现在给出超人能够跳的最远距离, 问: 如何摆放这些房子, 使得超人能够经过所有的房子跳到最高的房子, 又要使最矮的房子和最高的房子之间的距离最远(指房子在直线上的距离,不是走的路)??


摘自别人的分析:

看sample说明问题

sample3

4 2

10 20 16 13

超人从10开始,跳到13,但是10和13的水平距离至少为3,但超人的水平限制距离是2,所以无论怎么移动都无法跳过去,输出-1

 

看sample1

4 4

20 30 10 40

我们先给这些屋子,按横坐标给他们标号

20(1)   30(2)   10(3)   40(4)

所以我们可以描述为,超人跳跃的顺序为3 --->1---->2---->4,那么我们要求的就是3号点和4号点的最远距离(前提是要保证超人能完成整个跳跃)

另外超人的跳跃是按照高度来的,所以超人跳跃的路径其实是唯一确定的。要超人完成整个跳跃,就要保证超人能从“当前点”跳向“下一个点”,所以两点的水平距离有一个限制

| "下一个点的坐标" - “当前点的坐标” | <= lim  ,这里有一个绝对值,因为表示的是距离,所以我们可以约定一下,消去绝对值

d[v] <= d[u]+lim  ,其中点v的坐标大于点u的坐标

再看sample1:

要从10(3)跳到20(1)

| 点3的坐标 - 点1的坐标| <= lim  ,约定为   d[3] - d[1] <= lim   ---->  d[3] <= d[1] + lim  ---->因而建立的有向边为 <1,3> , w = lim

要从30(2)跳到40(4)

|点2的坐标 - 点4的坐标| <= lim , 约定为  d[4] - d[2] <= lim   ---->   d[4]  <=  d[2] + lim  ----->因而建立有向边<2,4> , w = lim

另外还别漏了一点,对于相邻的两个点,它们的距离至少为1

例如

30(2)  10(3)

这两个点要满足 d[3] - d[2] >= 1  --->   d[2] - d[3]  <=  -1    --->  d[2]  <=  d[3] + (-1)   ----> 建立有向边  <3,2> , w = -1

这样就建立了图,例如sample,我们就是要求点3到点4的最短路

还注意一点,因为我们建图的时候是约定好的,有向边都是 标号小的点  --->  标号大的点(除开边权为-1的边),所以我们找最短路的时候也要约定从标号小的点 到  标号大的点

如果  起点标号 >  终点标号   ,  则反过来求最短路 


再来说下我自己的看法: 做过上一题的裸题后做这题,自然就是建立约束关系的问题。无奈我不会。。一开始一直在尝试建立相邻高度之间的约束关系,也考虑了相邻两点之间的距离必须大于等于1的条件。但是我的那种模型应该是不正确的。按照正确解法的思路,必须把往左走和往右走的区分开来(即必须符号不同),因为最后需要求的距离肯定是正的,所以可以把往左跳定为负,往右跳定为正。
比如例子1的图:

如果求的是从高度为10的房子到高度为30的房子的距离,即3号点到2号点的距离,那么可以明显的看出距离就是2。
如果求的是从高度为10的房子到高度为40的房子的距离,那么从3号点走到2号点再走到4号点,就可以看出距离是3。
其实在这个例子中,对于高度为10的房子与高度为40的房子(即3号点与4号点)之间的距离的影响因素在于高度为30的房子的点所在的位置。
还是自己意会一下吧。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 1005;
int dis[maxn], head[maxn], time[maxn], cnt;
bool vis[maxn];
struct Edge
{
    int to, w, next;
}edge[2005];
struct House
{
    int hei, id;
}hou[maxn];
bool cmp(House p, House q)
{
    return p.hei < q.hei;
}
void add_edge(int u, int v, int w)
{
    edge[cnt].to = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
int spfa(int s, int en, int n)
{
    memset(dis, 63, sizeof(dis));
    memset(vis, false, sizeof(vis));
    memset(time, 0, sizeof(time));
    dis[s] = 0;
    vis[s] = true;
    time[s]++;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int tem = q.front();
        q.pop();
        vis[tem] = false;
        for(int i = head[tem]; i != -1; i = edge[i].next)
        {
            int to = edge[i].to;
            if(dis[tem] + edge[i].w < dis[to])
            {
                dis[to] = dis[tem] + edge[i].w;
                if(!vis[to])
                {
                    q.push(to);
                    vis[to] = true;
                    time[to]++;
                    if(time[to] >= n)
                    {
                        return -1;
                    }
                }
            }
        }
    }
    return dis[en];
}
int main()
{
    int T, n, dis, ca = 1;
    cin >> T;
    while(T--)
    {
        cin >> n >> dis;
        cnt = 0;
        memset(head, -1, sizeof(head));
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &hou[i].hei);
            hou[i].id = i;
            if(i >= 2)
                add_edge(i, i - 1, -1);
        }
        sort(hou + 1, hou + n + 1, cmp);
        for(int i = 1; i < n; i++)
        {
            int u = hou[i].id;
            int v = hou[i+1].id;
            if(u > v)
                swap(u, v);
            add_edge(u, v, dis);
        }
        int s = hou[1].id;
        int en = hou[n].id;
        if(s > en)
            swap(s, en);
        int ans = spfa(s, en, n);
        printf("Case %d: ", ca++);
        if(ans == -1)
            cout << -1 << endl;
        else
            cout << ans << endl;
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值