【dp-紫书例题】[11.17] 9-1 城市里的间谍

9-1 城市里的间谍

S1:审题
“最少等待时间”(最短路),给定的车站数n
需要进行决策:如果上了火车能使以后等待的时间减少,就上,否则等待

S2:确定DP数组的含义

int dp[i][j];
//表示状态“时刻i,在车站j”最少还需要等待的时间
//显然,本题答案为 dp[0][1]

S3:确定边界条件

for(int j=1;j<=n-1;j++) dp[T][j]=INF;
dp[T][n]=0;
//如果T时刻还没赶到车站n,等待时间是无穷
//如果T时刻在车站n,等待时间为0

S4:确定 i,j 的递推顺序

显然 j 正推逆推均可。
由于本题固定了起点与终点(要从车站1出发,到达车站n),直觉上易得dp[T-1][ j ]的答案:只要遍历j,就可以得出T-1时刻在各个车站的最优解——要么等1分钟,状态转移到dp[T][ j ],无解;要么恰好在j车站能登上列车,这辆列车可以在1分钟内到达车站n,状态转移到dp[T][n],等待时长0分钟。
继续考虑dp[T-2][ j ]的答案:要么等1分钟,状态转移到dp[T-1][ j ];要么上车(如果有。车程1分钟或2分钟均可。又因为所有车速度都一样,所以这个选择其实是在选上左向车还是右向车),状态转移到dp[T-1][ j ]或dp[T][ j ]。
以上转移后状态的值,已经在i=T及i=T-1时计算过,所有值均是最优解,逆推下去,就能得dp[0][1]的最优解了。
本题中 i 应该为逆推。

注意,其实是DP数组的含义决定了i的递推顺序(?此处正确性存疑)。如果i正推,那么DP数组的含义也相应变成“在i时刻到达车站j时已等待的最少时间”,这意味着可能无法到达车站n,需要用一个特殊数值来标记“无法到达”,相当麻烦。

S5:确定具体决策
显然,要么在车站等待,要么上左向车,要么上右向车。
对于一个还没算过的dp[i][j] (i<T且j<=n),要从三种决策中选择一个最小值为它赋值。由于i每次递减1,所以等待的值是确定的1分钟,同时还有加上状态转移后的等待时间,才是“i时刻在j车站还需要的最小等待时间”。这样dp[i][j]就有了一个确定的值,接下来再与上车的两种选择比较,来决定是否改变决策,更新这个dp[i][j]。

S6:补充计算
怎样才能知道i时刻在车站j能否上车?
1.要刚好有车可上;
2.上车之后不会超时;
3.车站n不能上右向车,车站1不能上左向车;

列车时刻信息通过已知条件经过处理之后,用一个三维数组保存:

int train[d][i][j]//d取值1或2,区分左向车与右向车
//i时刻,车站j;
//取值0或1,表示无车/有车

易错警告:
1.每组数据开始计算之前所有数组都要初始化!(debug30min(悲
2.有固定文字输出要求的一定要复制粘贴!!自己手打Case Number: 的时候少打了个空格(浪费5min

#include<stdio.h>
#include<algorithm>
#include<string.h>
#define INF 0x3f3f3f3f
using namespace std;
int train[3][10010][55];
int t[55],t1[55],t2[55];
int dp[10010][55];
//dp[i][j]表示在i时刻在j车站最少还需要等待的时间
//即以[i][j]为起点 
int main()
{
	int kase=1,n;
	while(scanf("%d",&n)==1&&n!=0){
		memset(dp,0,sizeof(dp));
		memset(t, 0,sizeof(t));
		memset(t1,0,sizeof(t1));
		memset(t2,0,sizeof(t2));
        memset(train, 0, sizeof(train));
		int T,M1,M2,d;
		scanf("%d",&T);
		for(int i=1;i<=n-1;i++) scanf("%d",&t[i]); 
		//t[i]为从车站i到车站i+1的行驶时间 
		//若在d时刻从车站1出发,那么在d+t[1]时刻列车在车站2 
		for(int i=2;i<=n;i++)  t1[i]=t1[i-1]+t[i-1];
		for(int i=n-1;i>=1;i--) t2[i]=t2[i+1]+t[i];
		//分别处理,使得t[i]表示从车站1/车站n出发 到车站[i]的时间 
		scanf("%d",&M1);
		for(int i=1;i<=M1;i++){
			scanf("%d",&d);
			for(int j=1;j<=n;j++){
				train[1][d+t1[j]][j]=1; 
			}
		}
		scanf("%d",&M2);
		for(int i=1;i<=M2;i++){
			scanf("%d",&d);
			for(int j=1;j<=n;j++){
				train[2][d+t2[j]][j]=1; 
	 		}
		}
		
		for(int i=1;i<=n-1;i++) dp[T][i]=INF;
		dp[T][n]=0;
		//已经到了约定时刻T,在车站n以外的任何车站显然已经无法到达
		//已经在车站n,等待时间为0 
		
		//从T时刻的前一分钟开始递推 
		for(int i=T-1;i>=0;i--)
			for(int j=1;j<=n;j++){
				//决策1:等1单位时间 
				dp[i][j]=dp[i+1][j]+1;
				//决策2:上向右开的列车前往下一站(如果有且不超时) 
				if(j<n&&train[1][i][j]&&i+t[j]<=T)
					dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);
				//决策3:上向左开的列车前往上一站 (如果有且不超时)
				if(j>1&&train[2][i][j]&&i+t[j-1]<=T) 
					dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);
			}
		
		printf("Case Number %d: ",kase);
		if(dp[0][1]>=INF) printf("impossible\n");
		else printf("%d\n",dp[0][1]);
		kase++;
	}
	return 0;
}
	
	
发布了28 篇原创文章 · 获赞 0 · 访问量 673

猜你喜欢

转载自blog.csdn.net/weixin_45561591/article/details/103108080
9-1