例题9-1 城市里的间谍(A Spy in the Metro, UVa1025)

欢迎访问我的Uva题解目录 https://blog.csdn.net/richenyunqi/article/details/81149109

题目描述

例题9-1 城市里的间谍(A Spy in the Metro, UVa1025)_第1张图片

题意解析

某城市的地铁是线性的,有n(2≤n≤50)个车站,从左到右编号为1~n。有M1辆列车从第1站开始往右开,还有M2辆列车从第n站开始往左开。在时刻0,Mario从第1站出发,目的是在时刻T(0≤T≤200)会见车站n的一个间谍。在车站等车时容易被抓,所以她决定尽量躲在开动的火车上,让在车站等待的总时间尽量短。列车靠站停车时间忽略不计,且Mario身手敏捷,即使两辆方向不同的列车在同一时间靠站,Mario也能完成换乘。
输入第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行。输出仅包含一行,即最少等待时间。无解输出impossible。

算法设计

参考《算法竞赛入门经典(第2版)》中的分析:

时间是单向流逝的,是一个天然的“序”。影响到决策的只有当前时间和所处的车站,所以可以用d(i,j)表示时刻i,你在车站j(编号为1~n),最少还需要等待多长时间。边界条件是d(T,n)=0,其他d(T,i)(i不等于n)为正无穷。
有如下3种决策:
决策1:等1分钟。
决策2:搭乘往右开的车(如果有)。
决策3:搭乘往左开的车(如果有)。

我是用d(i,j)表示在车站i和时刻t时所需的最短等待时间。可通过两种方法求解,一种是记忆化搜索的方法,一种是递推的方法。下面给出这两种算法的具体实现代码。

记忆化搜索的C++代码

#include
using namespace std;
int N,T,m1,m2,t[55],dp[55][205];
bool hasTrain[55][205][2];
const int INF=(int)(1e9);
int DP(int i,int j){
    if(i==N&&j==T)//到达终点
        return dp[i][j]=0;
    else if(i!=N&&j==T)//到达边界
        return dp[i][j]=INF;
    if(dp[i][j]!=0)//该状态已经计算过了
        return dp[i][j];
    dp[i][j]=DP(i,j+1)+1;//第一种决策
    if(i+1<=N&&j+t[i]<=T&&hasTrain[i][j][0])//第二种决策
        dp[i][j]=min(dp[i][j],DP(i+1,j+t[i]));
    if(i-1>=1&&j+t[i-1]<=T&&hasTrain[i][j][1])//第三种决策
        dp[i][j]=min(dp[i][j],DP(i-1,j+t[i-1]));
    return dp[i][j];
}
int main(){
    for(int ii=1;~scanf("%d",&N)&&N!=0;++ii){
        memset(hasTrain,0,sizeof(hasTrain));//将hasTrain数组均置为false
        memset(dp,0,sizeof(dp));//将dp数组均置为0
        scanf("%d",&T);
        for(int i=1;i<N;++i)
            scanf("%d",&t[i]);
        int a;
        scanf("%d",&m1);
        for(int i=0;i<m1;++i){
            scanf("%d",&a);
            for(int j=1;j<N;++j){//计算该列车到达每个站的时刻,并置hasTrain为true
                if(a<=T)
                    hasTrain[j][a][0]=true;
                a+=t[j];
            }
        }
        scanf("%d",&m2);
        for(int i=0;i<m2;++i){
            scanf("%d",&a);
            for(int j=N;j>1;--j){//计算该列车到达每个站的时刻,并置hasTrain为true
                if(a<=T)
                    hasTrain[j][a][1]=true;
                a+=t[j-1];
            }
        }
        DP(1,0);
        printf("Case Number %d: ",ii);
        if(dp[1][0]>=INF)
            puts("impossible");
        else
            printf("%d\n",dp[1][0]);
    }
    return 0;
}

递推的C++代码

#include
using namespace std;
int N,T,m1,m2,t[55],dp[55][205];
bool hasTrain[55][205][2];
const int INF=(int)(1e9);
int main(){
    for(int ii=1;~scanf("%d",&N)&&N!=0;++ii){
        memset(hasTrain,0,sizeof(hasTrain));//将hasTrain数组均置为false
        scanf("%d",&T);
        for(int i=1;i<N;++i)
            scanf("%d",&t[i]);
        int a;
        scanf("%d",&m1);
        for(int i=0;i<m1;++i){
            scanf("%d",&a);
            for(int j=1;j<N;++j){//计算该列车到达每个站的时刻,并置hasTrain为true
                if(a<=T)
                    hasTrain[j][a][0]=true;
                a+=t[j];
            }
        }
        scanf("%d",&m2);
        for(int i=0;i<m2;++i){
            scanf("%d",&a);
            for(int j=N;j>1;--j){//计算该列车到达每个站的时刻,并置hasTrain为true
                if(a<=T)
                    hasTrain[j][a][1]=true;
                a+=t[j-1];
            }
        }
        for(int i=1;i<N;++i)//处理边界值
            dp[i][T]=INF;
        dp[N][T]=0;//终点
        for(int j=T-1;j>=0;--j)
            for(int i=1;i<=N;++i){
                dp[i][j]=dp[i][j+1]+1;//第一种决策
                if(i+1<=N&&j+t[i]<=T&&hasTrain[i][j][0])//第二种决策
                    dp[i][j]=min(dp[i][j],dp[i+1][j+t[i]]);
                if(i-1>=1&&j+t[i-1]<=T&&hasTrain[i][j][1])//第三种决策
                    dp[i][j]=min(dp[i][j],dp[i-1][j+t[i-1]]);
            }
        printf("Case Number %d: ",ii);
        if(dp[1][0]>=INF)
            puts("impossible");
        else
            printf("%d\n",dp[1][0]);
    }
    return 0;
}

你可能感兴趣的:(算法竞赛入门经典,-,Uva)