C.张老师的旅行
链接:https://ac.nowcoder.com/acm/contest/5477/C

题目描述

张老师到了一个王国去旅游,王国有n个景点,张老师到达这个城市所在的车站恰好位于第x个景点,这个王国非常特别,恰好所有著名的景点都在分布在直线上,每个景点在坐标pi上(单位:公里),张老师身体非常好,每走一公里花费一分钟。每个景点都有一个打卡点,并且必须在不迟于相应的时间(时间从张老师到达王国开始计算)前到达才能打卡成功并且给以一个打卡标记,集齐所这些标记就能获得一个大礼包。由于张老师非常想要大礼包,并且因为张老师还着急去下一个王国旅游,所以张老师希望用的时间尽量少,你能帮帮张老师吗?

输入描述:

输入的第一行,包含一个整数n(1≤n≤1000)。
第二行包含n个整数pi(1≤pi≤100000),第i个整数pi为第i个景点的坐标(坐标从小到大排列)。
最后一行包含n个整数ti(0≤ti≤10,000,000),ti表示第i个景点最迟到达的时间,时间为0则表示张老师所在车站的位置且只有一个为0。

输出描述:

输出所需的最少时间,若无解输出-1。

示例1
输入

4
1 2 3 4
0 1 2 3

输出

3

思路
我们把所有点放在数轴上,显然这些点被k点分为两各部分,不妨设这左右两部分为b[N],c[N],并用b[i].len表示左边各点到k点距离,用c[j].len表示右边各个点到k点距离,b[i].t和c[j].t表示该点最晚到达时间,然后b[N],c[N]分别按到k点距离排序,这样我们得到两组数据,再对这两组数据进行dp,
我们设dp[i][j]为完成b(左)中前i个点,和c(右)中前j个点的最小时间,但是我们考虑到最终的落脚点可以在b(左边)中,也可在c(右边)中,所以加上两种状态,表示落脚点在哪一边,即dp[i][j][2],我们可以写出dp方程:

 dp[i][j][0]=min(dp[i-1][j][0]+b[i].l-b[i-1].l,dp[i-1][j][1]+c[j].l+b[i].l);
 dp[i][j][1]=min(dp[i][j-1][1]+c[j].l-c[j-1].l,dp[i][j-1][0]+c[j].l+b[i].l);

(如果看不太明白,可以上下画两条线段,自己模拟一下)
最短到达时间我们算出来了,只要对它跟b[i].t或c[j].t比较,就可以判断是否合法即:

if(dp[i][j][0]>b[i].t&&dp[i][j][1]>c[j].t){cout<<-1;return;}

当然如果只有一个不合法,我们要将该状态变为INF,消除它对后面的影响即:

if(dp[i][j][0]>b[i].t)dp[i][j][0]=INF;
if(dp[i][j][1]>c[j].t)dp[i][j][1]=INF;

附代码

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int dp[1001][1001][2];
struct node{
    int t=0,l=0;
}a[1001],b[1001],c[1001];
bool cmp(node x,node y){return x.l<y.l;}
int main(){
     int n;cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i].l;
    for(int i=1;i<=n;i++)cin>>a[i].t;
    sort(a+1,a+1+n,cmp);
    int k;
    for(int i=1;i<=n;i++){
        if(a[i].t==0)k=i;
    }
    memset(dp,INF,sizeof(dp));
    int x=0,y=0;
    dp[0][0][1]=dp[0][0][0]=0;
    for(int i=k-1;i>0;i--)x++,b[x].l=abs(a[i].l-a[k].l),b[x].t=a[i].t;//求左边点集
    for(int i=k+1;i<=n;i++)y++,c[y].l=abs(a[i].l-a[k].l),c[y].t=a[i].t;//求右边点集
    for(int i=0;i<=x;i++){
        for(int j=0;j<=y;j++){
            if(i)dp[i][j][0]=min(dp[i-1][j][0]+b[i].l-b[i-1].l,dp[i-1][j][1]+c[j].l+b[i].l);
            if(j)dp[i][j][1]=min(dp[i][j-1][1]+c[j].l-c[j-1].l,dp[i][j-1][0]+c[j].l+b[i].l);
            if(dp[i][j][0]>b[i].t&&dp[i][j][1]>c[j].t){cout<<-1;return 0;}
            if(dp[i][j][0]>b[i].t)dp[i][j][0]=INF;
            if(dp[i][j][1]>c[j].t)dp[i][j][1]=INF;
        }
    }
    cout<<min(dp[x][y][0],dp[x][y][1]);
}