欢迎访问我的Uva题解目录 https://blog.csdn.net/richenyunqi/article/details/81149109
某城市的地铁是线性的,有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
时所需的最短等待时间。可通过两种方法求解,一种是记忆化搜索的方法,一种是递推的方法。下面给出这两种算法的具体实现代码。
#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;
}
#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;
}