[动态规划]【ACM/ICPC Word Finals 2003, UVa 1025】A Spy in the Metro 城市里的间谍

题目描述

某城市地铁是线性的,有n个车站,从左到右编号1-n。有M1辆列车从第1站开始往右开,还有M2辆列车从第n站开始往左开。在时刻0,Mario从第1站出发,目的在时刻T会见车站n的一个间谍。在车站等车时容易被抓,所以她决定尽量躲在开动的火车上,让在车站等待的时间尽量短。列车靠站停车时间忽略不计,且Mario身手敏捷,即时两辆方向不同的列车在同一时间靠站,Mario也能完成换乘。

Input Data

输入第1行为n
第2行为T
第3行有n-1个整数t1,t2,…,tn−1(1≤ti≤70),其中ti表示地铁从车站i到i+1的行驶时间(两个方向一样)。
第4行为M1(1≤M1≤50),即从第1站出发向右开的列车数目。
第5行包含M1个整数d1, d2,…, dM1(0≤di≤250,di<di+1),即各列车的出发时间。
第6、7行描述从第n站出发向左开的列车,格式同第4、5行。

Output Data

输出仅包含一行,即最少等待时间。无解输出impossible。

Input

4
55
5 10 15
4
0 5 10 20
4
0 5 10 15
4
18
1 2 3
5
0 3 6 10 12
6
0 3 5 7 12 15
2
30
20
1
20
7
1 3 5 7 11 13 17
0

Output

Case Number 1: 5
Case Number 2: 0
Case Number 3: impossible

——————————————分割の线————————————————
已知时间i,Mario停留在j站。不难发现Mario之前到达j站的路径和时间与之后的决策并无关系,且决策仅与当前的时间和所处的车站有关。不妨用f[i][j]表示时刻i,Mario处于站台j时,最少还需要等待的时间。边界为f[T][n]=0,f[T][i]=+∞;
存在三种决策:

  1. 就地等一分钟
  2. 如果有车,搭乘向左开的车
  3. 如果有车,搭乘向右开的车

在主过程上,运用填表法:

 for(int i=T-1;i>=0;i--)
        {
            for(int j=1;j<=n;j++)
            {
                f[i][j]=f[i+1][j]+1;
                if(j<n&&has_train[i][j][0]&&i+t[j]<=T)
                    f[i][j]=min(f[i][j],f[i+t[j]][j+1]);//right
                if(j>1&&has_train[i][j][1]&&i+t[j-1]<=T)
                    f[i][j]=min(f[i][j],f[i+t[j-1]][j-1]);//left
            }
        }

此处运用has_train[t][i][0]表示时刻i,在站台j是否有向右的火车。has_train[t][i][0]与其类似,但是是记录是否有向左开的火车。
我在输入时进行处理:

        memset(has_train,0,sizeof(has_train));
        cin>>m1;
        for(int i=1;i<=m1;i++)
        {
            int sum;
            scanf("%d",&sum);//读入发车时间
            for(int j=1;j<=n;j++)
            {
                has_train[sum][j][0]=1;//记录在时刻sum站台j,有一辆向右开的火车
                sum+=t[j];//行驶到站台j+1的时间
            }
        }
        cin>>m2;
        for(int i=1;i<=m2;i++)
        {
            int sum;
            scanf("%d",&sum);//读入发车时间
            for(int j=n;j>=1;j--)//由于是从右向左开,所以反向枚举
            {
                has_train[sum][j][1]=1;//记录在时刻sum站台j,有一辆向左开的火车
                sum+=t[j-1];//行驶到站台j-1的时间
            }
        }

完整过程如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 0x11111111
int t[100];
int f[2000][100],has_train[2000][100][2];
int main()
{
    int n,T,m1,m2,cnt=0,sick=0;
    while(cin>>n&&n!=0)
    {
        memset(t,0,sizeof(t));
        memset(has_train,0,sizeof(has_train));
        cnt++;
        cin>>T;
        for(int i=1;i<n;i++)
            scanf("%d",&t[i]);//读入从i到i+1的行驶时间,用t[i]表示
        cin>>m1;
        for(int i=1;i<=m1;i++)
        {
            int sum;
            scanf("%d",&sum);//读入发车时间
            for(int j=1;j<=n;j++)
            {
                has_train[sum][j][0]=1;//记录在时刻sum站台j,有一辆向右开的火车
                sum+=t[j];//行驶到站台j+1的时间
            }
        }
        cin>>m2;
        for(int i=1;i<=m2;i++)
        {
            int sum;
            scanf("%d",&sum);//读入发车时间
            for(int j=n;j>=1;j--)//由于是从右向左开,所以反向枚举
            {
                has_train[sum][j][1]=1;//记录在时刻sum站台j,有一辆向左开的火车
                sum+=t[j-1];//行驶到站台j-1的时间
            }
        }
        for(int i=1;i<n;i++)
            f[T][i]=INF;
        f[T][n]=0;//以上均为初始化
        for(int i=T-1;i>=0;i--)
        {
            for(int j=1;j<=n;j++)
            {
                f[i][j]=f[i+1][j]+1;
                if(j<n&&has_train[i][j][0]&&i+t[j]<=T)
                    f[i][j]=min(f[i][j],f[i+t[j]][j+1]);//right
                if(j>1&&has_train[i][j][1]&&i+t[j-1]<=T)
                    f[i][j]=min(f[i][j],f[i+t[j-1]][j-1]);//left
            }
        }
        printf("Case Number %d: ",cnt);
        if(f[0][1]>=INF) printf("impossible\n");//只有时刻0并从站台1出发,才合法
        else printf("%d\n",f[0][1]);
    }
    return 0;
}

状态有O(nT)个,每个状态最多只有3个决策,因此总时间复杂度为O(nT).

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值