城市里的间谍(A Spy in the Metro UVa 1025)最详细题解

累加器传送门:

http://blog.csdn.net/NOIAu/article/details/71775000

题目传送门:

https://vjudge.net/problem/UVA-1025

题目内容:

Secret agent Maria was sent to Algorithms City to carry out an especially dangerous mission. After
several thrilling events we nd her in the rst station of Algorithms City Metro, examining the time
table. The Algorithms City Metro consists of a single line with trains running both ways, so its time
table is not complicated.
Maria has an appointment with a local spy at the last station of Algorithms City Metro. Maria
knows that a powerful organization is after her. She also knows that while waiting at a station, she is
at great risk of being caught. To hide in a running train is much safer, so she decides to stay in running
trains as much as possible, even if this means traveling backward and forward. Maria needs to know
a schedule with minimal waiting time at the stations that gets her to the last station in time for her
appointment. You must write a program that nds the total waiting time in a best schedule for Maria.
The Algorithms City Metro system has N stations, consecutively numbered from 1 to N. Trains
move in both directions: from the rst station to the last station and from the last station back to the
rst station. The time required for a train to travel between two consecutive stations is xed since all
trains move at the same speed. Trains make a very short stop at each station, which you can ignore
for simplicity. Since she is a very fast agent, Maria can always change trains at a station even if the
trains involved stop in that station at the same time.

输入:

The input le contains several test cases. Each test case consists of seven lines with information as
follows.
Line 1. The integer N (2 N 50), which is the number of stations.
Line 2. The integer T (0 T 200), which is the time of the appointment.
Line 3. N 1 integers: t1; t2; : : : ; tN1 (1 ti 20), representing the travel times for the trains
between two consecutive stations: t1 represents the travel time between the rst two stations, t2
the time between the second and the third station, and so on.
Line 4. The integer M1 (1 M1 50), representing the number of trains departing from the rst
station.
Line 5. M1 integers: d1; d2; : : : ; dM1 (0 di 250 and di < di+1), representing the times at which
trains depart from the rst station.
Line 6. The integer M2 (1 M2 50), representing the number of trains departing from the N-th
station.
Line 7. M2 integers: e1; e2; : : : ; eM2 (0 ei 250 and ei < ei+1) representing the times at which
trains depart from the N-th station.
The last case is followed by a line containing a single zero.

输出:

For each test case, print a line containing the case number (starting with 1) and an integer representing
the total waiting time in the stations for a best schedule, or the word `impossible’ in case Maria is
unable to make the appointment. Use the format of the sample output.

题的核心是逆推的思想

这道题一看要问最少需要等待多少时间,自然会想到用dp,那么怎么来处理这个问题呢?我们可以用dp[i][j]来表示时刻i,你现在身处第j个站,最少还需要等待多长时间,我们所知的是,在T时刻,你人一定需要在第n个车站去完成间谍任务,所以dp[T][n]=0;可以由这个位置来进行反推,比如我在时刻T-1可以仍然位于车站n或者说如果有往左开的车我就有选择往左坐车的可能;
那么我们对于一个dp[i][j]进行状态转移,有三种转移方式:

决策一:

是我呆在原地不动,这个时候需要等的时间是由dp[i+1][j]转移而来,需要在dp[i+1][j]的基础上+1;因为我在j+1的时候的最优等待值的基础上,等待一个时间(时间从后往前逆推,注意dp数组表示的是最少还需要等待多少时间)

dp[i][j]=dp[i+1][j]+1;

决策二:

往右移动,如果往右有车的话
那么dp[i][j]就需要从dp[i+t[j]][j+1]转移过来
dp[i+t[j]][j+1]表示的是时刻i+t[j]的时候(相当于是往右到下一站j+1站的时候的最优值),由于是逆推的,所以dp[i+t[j]][j+1]的值先于dp[i][j]计算出,所以dp转移合理

dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);

决策三:

往左移动,如果往左有车的话
那么dp[i][j]就需要从dp[i+t[j-1]][j-1]转移过来,原理和上面一样,往左需要花t[j-1]的时间,i+t[j-1]的时刻,位置在左一站的j-1站时的最优值,由于循环里先循环的是i,而且是逆向循环,所以dp[i+t[j-1]][j-1]的值也先已经计算得出,所以dp转移合理

dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);

那么如何判定在某一个时刻有没有车往左或者往右已进行dp转移呢,我们可以先进行预处理,用一个has_a _train[i][j][2]数组来存储,其中has_a _train[i][j][0]表示i时刻在j车站,有没有一辆向左开的地铁,has_a _train[i][j][1]则表示i时刻在j车站,有没有一辆向右开的地铁
预处理和输出的代码如下:

bool init(){
    memset(has_a_train,false,sizeof(has_a_train));
    scanf("%d",&n);
    if(n==0) return false;
    scanf("%d",&T);
    for(int i=1;i<=n-1;i++) scanf("%d",&t[i]);
    scanf("%d",&M1);
    for(int i=1;i<=M1;i++){
        scanf("%d",&dl[i]);
        int timer=dl[i];
        if(timer<=T) has_a_train[timer][1][1]=true;
        for(int i=1;i<=n-1;i++){
            if(timer+t[i]<=T){
                has_a_train[timer+t[i]][i+1][1]=true;
                timer+=t[i];
            }
            else break;
        }
    }
    scanf("%d",&M2);
    for(int i=1;i<=M2;i++){
        scanf("%d",&dr[i]);
        int timer=dr[i];
        if(dr[i]<=T) has_a_train[timer][n][0]=true;
        for(int i=n-1;i>=1;i--){
            if(timer+t[i]<=T){
                has_a_train[timer+t[i]][i][0]=true;
                timer+=t[i];
            }
            else break;
        }
    }
    return true;
}

进行预处理之后我们就可以进行O(1)的dp转移了,这样会变得非常方便,具体的dp转移是这样的

void dpp(){
    for(int i=1;i<=n-1;i++) dp[T][i]=MAXNN;
    dp[T][n]=0;
    for(int i=T-1;i>=0;i--){
        for(int j=1;j<=n;j++){
            dp[i][j]=dp[i+1][j]+1;
            if(j<n&&has_a_train[i][j][1]&&i+t[j]<=T)
            dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);
            if(j>1&&has_a_train[i][j][0]&&i+t[j-1]<=T)
            dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);
        }
    }
}

所以到这里应该已经完全明白了这道题
现在贴上完整代码

#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 1000+10
#define MINN 100+5
#define MAXNN 10000000+10
using namespace std;

int dl[MAXN],dr[MAXN],t[MAXN],n,T,M1,M2;
bool has_a_train[MAXN][MAXN][2];
int dp[MAXN][MINN];
int cnt;

bool init(){
    memset(has_a_train,false,sizeof(has_a_train));
    scanf("%d",&n);
    if(n==0) return false;
    scanf("%d",&T);
    for(int i=1;i<=n-1;i++) scanf("%d",&t[i]);
    scanf("%d",&M1);
    for(int i=1;i<=M1;i++){
        scanf("%d",&dl[i]);
        int timer=dl[i];
        if(timer<=T) has_a_train[timer][1][1]=true;
        for(int i=1;i<=n-1;i++){
            if(timer+t[i]<=T){
                has_a_train[timer+t[i]][i+1][1]=true;
                timer+=t[i];
            }
            else break;
        }
    }
    scanf("%d",&M2);
    for(int i=1;i<=M2;i++){
        scanf("%d",&dr[i]);
        int timer=dr[i];
        if(dr[i]<=T) has_a_train[timer][n][0]=true;
        for(int i=n-1;i>=1;i--){
            if(timer+t[i]<=T){
                has_a_train[timer+t[i]][i][0]=true;
                timer+=t[i];
            }
            else break;
        }
    }
    return true;
}

void dpp(){
    for(int i=1;i<=n-1;i++) dp[T][i]=MAXNN;
    dp[T][n]=0;
    for(int i=T-1;i>=0;i--){
        for(int j=1;j<=n;j++){
            dp[i][j]=dp[i+1][j]+1;
            if(j<n&&has_a_train[i][j][1]&&i+t[j]<=T)
            dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);
            if(j>1&&has_a_train[i][j][0]&&i+t[j-1]<=T)
            dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);
        }
    }
}

void print(){
    cnt++;
    printf("Case Number %d: ",cnt);
    if(dp[0][1]>=MAXNN) printf("impossible\n");
    else printf("%d\n",dp[0][1]);
}

int main(){
    while(init()){
        dpp();
        print();
    }
    return 0;
}

这里写图片描述

一定要注意has_a _train数组的初始化

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值