某城市地铁是线性的,有n个车站,从左到右编号1-n。有M1辆列车从第1站开始往右开,还有M2辆列车从第n站开始往左开。在时刻0,Mario从第1站出发,目的在时刻T会见车站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。
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
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]=+∞;
存在三种决策:
在主过程上,运用填表法:
for(int i=T-1;i>=0;i--)
{
for(int j=1;j<=n;j++)
{
f[i][j]=f[i+1][j]+1;
if(j0]&&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
#include
#include
#include
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;iscanf("%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;i0;//以上均为初始化
for(int i=T-1;i>=0;i--)
{
for(int j=1;j<=n;j++)
{
f[i][j]=f[i+1][j]+1;
if(j0]&&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).