[机房练习赛4.5]note

Problem 1. note
Input file: note.in
Output file: note.out
Time limit: 1 second
最近有一款很火的游戏,叫做八分音符酱,它和马里奥很相似,不过它的跳跃距离是由你的声音大小来控制
的。不过我们现在对玩法就行一些修改:
现有一共有n 个柱子,两个相邻的柱子之间的初始水平距离为1,蠢蠢的jyb 现在在最矮的柱子上,他每次
只能向恰好比这个柱子高的另一个柱子跳跃,最后要跳到最高的柱子上。
jyb 需要从第二个柱子跳到第一个柱子,再跳到第三个柱子
jyb 的最大声音为d,代表他能在满足jposi �� posj j + jheighti �� heightj j d 的两个柱子之间跳跃。假设我
们可以在不改变它们位置的相对顺序的前提下水平移动柱子,调整他们的水平位置(但相邻间隔至少为1,且
为整数)。for example
d=4
jyb 想问你:在能从最矮柱子跳到最高柱子的前提下,水平移动柱子,最矮柱子和最高柱子的最大水平距离可
以达到多少。
Input
第1 行,1 个整数T, 表示数据组数,对于每组数据:
第1 行,1 个整数n; d,表示一共有多少个柱子和最大声音。
接下来1 行,有n 个数,hi 表示柱子的高度。保证柱子高度互不相同
Output
对于每组数据,输出在能从最矮柱子跳到最高柱子的前提下,水平移动柱子,最矮柱子和最高柱子的最大水
平距离可以达到多少。如果不能,输出-1
2
Sample
note.in note.out
2
3 4
3 2 4
3 4
3 2 6
2
-1
note.in note.out
2
5 10
4 2 1 8 10
5 2
10 8 2 1 4
12
-1
Note
• 对于第一个样例的第一组数据的解释:如题面的例子,高度为2 的柱子和高度为4 的柱子之间距离为
2,高度为2 的柱子和高度为3 的柱子距离为1。
• 对于30% 的数据,1 n 100,1 d 1000,1 hi 1000;
• 对于100% 的数据,1 T 100,1 n 103,1 d 106,1 hi 106。

note:差分约束系统
我们不能随意地将水平位置拉开,因为必须要能够从最矮的柱子跳到最高的柱子,其实就相当于有了很多限制条件,显然这是一个差分约束系统。
对于构图,我们对于需要跳的两个柱子i,j,我们建一条从i到j的边,边权为 ,在这里判断一下是否能跳(边权为小于相对初始水平距离,那么答案就是-1,如果不在这里判,通过跑图中是否有负权环会有三个点TLE),注意到这里我们约定了:不管是从i跳到j还是j跳到i,都是从i到j建边,所以我们还需要对所有相邻的两个柱子建一条i+1到i,边权为-1的边。
建好图后,跑最短路即可。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int maxn = 1005;
int T,n,d,ans;
int st,ed,u,v;
inline int read() {
    int x = 0, f = 1;char ch = getchar();
    while( ch < '0' || ch > '9'){if(ch == '-')f=-1;ch=getchar();}
    while(ch>='0' && ch <='9'){x = x*10+ch-'0';ch = getchar();}
    return x*f;
}
struct node{
    int pos,h;
    bool operator <(const node &b) const{ return h < b.h; }
}a[maxn];
struct edge{
    int v,next,w;
}e[maxn<<2];
int h[maxn],cnt;
void adde( int u, int v, int w ){
    e[++cnt].v = v;e[cnt].w = w;e[cnt].next = h[u]; h[u] = cnt;
}
int dis[maxn];
bool vis[maxn];
queue<int>q;
int bb[maxn];
int spfa( int st, int ed ){
    memset(dis,65,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(bb,0,sizeof(bb));
    q.push(st);
    dis[st] = 0;vis[st] = true; bb[st] = 1;
    while( !q.empty() ){
        u = q.front(); q.pop();
        for( int i = h[u]; i; i = e[i].next ){
            v = e[i].v;
            if( dis[v] > dis[u] + e[i].w ){
                dis[v] = dis[u] + e[i].w;
                if( !vis[v] ){
                    bb[v]++;
                    vis[v] = true;
                    q.push(v);
                    if( bb[v] > n ) return -1;
                }
            }
        }
        vis[u] = false;
    }
    return dis[ed];
}
int main(){
    freopen("note.in","r",stdin);
    freopen("note.out","w",stdout);
    scanf("%d", &T);
    while( T-- ){
        n = read();d = read();
        for( int i = 1; i <= n; i++ ){
            a[i].h = read();
            a[i].pos = i;
        }
        bool ans = true;
        std::sort(a+1,a+n+1);
        st = min(a[1].pos,a[n].pos);
        ed = max(a[1].pos,a[n].pos);
        memset(h,0,sizeof(h));
        cnt = 0;
        for( int i = 1; i < n; i++ ){
            adde(i+1,i,-1);
            u = min(a[i].pos,a[i+1].pos);
            v = max(a[i].pos,a[i+1].pos);
            if(d < v - u + a[i+1].h - a[i].h){ans = false;break;}
            adde( u, v, d - a[i+1].h + a[i].h);
        }
        if( ans ) printf("%d\n",spfa(st,ed));
        else printf("-1\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值