算法竞赛入门经典--例题和课后训练(动态规划)
Posted tech-chen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法竞赛入门经典--例题和课后训练(动态规划)相关的知识,希望对你有一定的参考价值。
1.地铁间谍(逆序推导时间法)
特工玛利亚被送到S市执行一个特别危险的任务。她需要利用地铁完成他的任务,S市的地铁只有一条线路运行,所以并不复杂。
玛利亚有一个任务,现在的时间为0,她要从第一个站出发,并在最后一站的间谍碰头。玛利亚知道有一个强大的组织正在追踪她,她知道如果一直呆在一个车站,她会有很大的被抓的风险,躲在运行的列车中是比较安全的。所以,她决定尽可能地呆在运行的列车中,她只能往前或往后坐车。
玛利亚为了能准时且安全的到达最后一个车站与对方碰头,需要知道在在车站最小等待时间总和的计划。你必须写一个程序,得到玛丽亚最短的等待时间。当然,到了终点站之后如果时间还没有到规定的时刻,她可以在车站里等着对方,只不过这个等待的时刻也是要算进去的。
这个城市有n个车站,编号是1-n,火车是这么移动的:从第一个车站开到最后一个车站。或者从最后一站发车然后开会来。火车在每特定两站之间行驶的时间是固定的,我们也可以忽略停车的时间,玛利亚的速度极快,所以他可以迅速上下车即使两辆车同时到站。
输入输出格式
输入格式:输入文件包含多组数据,每组数据都由7行组成
第1行:一个正整数N(2<=N<=50)表示站的数量
第2行:一个正整数T(0<=T<=200)表示需要的碰头时间
第3行:1-(n-1)个正整数(0<ti<70)表示两站之间列车的通过时间
第4行:一个整数M1(1<=M1<=50)表示离开第一个车站的火车的数量
第5行:M1个正整数:d1,d2……dn,(0<=d<=250且di<di+1)表示每一列火车离开第一站的时间
第6行:一个正整数M2(1<=M2<=50)表示离开第N站的火车的数量
第7行:M2个正整数:e1,e2……eM2,(0<=e<=250且ei<ei+1)表示每一列火车离开第N站的时间
最后一行有一个整数0。
输出格式:对于每个测试案例,打印一行“Case Number N: ”(N从1开始)和一个整数表示总等待的最短时间或者一个单词“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
说明
第一组样例说明,她0分钟时上车,在3号站下车,立刻坐上(0分始发)15分开的车回去,到2号车站,立刻坐上(20分始发)25开的车到终点,50分到,还需要等待5分钟。
/*时间上的逆推法:由下一刻到t的最短时间来求当前时刻的最短时间, 注意这个题目中的预处理,解决了DP过程中需要判断能不能坐火车的问题(其他题目也要相互类比一下) */ #include<iostream> using namespace std; #include<cstdio> #define N 501 #define T 2001 #define INF 1<<30 #include<cstring> int n,t,tim[N],m1,trainm1[N],m2,trainm2[N]; int have_train[T][N][2]; int kase=0; int f[T][N]; void input() { memset(tim,0,sizeof(tim)); memset(trainm1,0,sizeof(trainm1)); memset(trainm2,0,sizeof(trainm2)); memset(have_train,0,sizeof(have_train)); memset(f,0,sizeof(f)); scanf("%d",&t); for(int i=1;i<=n-1;++i) { scanf("%d",&tim[i]); } scanf("%d",&m1); for(int i=1;i<=m1;++i) { scanf("%d",&trainm1[i]); int k=1; for(int j=trainm1[i];k<=n;j+=tim[k-1]) { have_train[j][k][0]=1;/*have_train[][][]记录第j时刻,第k个车站,0表示向右的地铁,1表示向左的地铁,这是预处理,DP的时候容易写出方程*/ k++; } } scanf("%d",&m2); for(int i=1;i<=m2;++i)/*同样的预处理*/ { scanf("%d",&trainm2[i]); int k=n; for(int j=trainm2[i];k>=1;j+=tim[k]) { have_train[j][k][1]=1; k--; } } } void DP() { for(int i=1;i<=n-1;++i) f[t][i]=INF;/*动规方程的边界条件,在第t时刻,到达了n车站的最小等待时间是0*/ 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&&have_train[i][j][0]&&i+tim[j]<=t)/*递推:在i时刻j车站有向右的地铁,那么乘车走向下一站,时间从下一站推导这一站*/ f[i][j]=min(f[i][j],f[i+tim[j]][j+1]); if(j>1&&have_train[i][j][1]&&i+tim[j-1]<=t) f[i][j]=min(f[i][j],f[i+tim[j-1]][j-1]); } } int main() { for(;;) { scanf("%d",&n); if(n==0) break; ++kase; printf("Case Number %d: ",kase); input(); DP(); if(f[0][1]>=INF)/*如果推出的在第0时刻,第一个点到终点的最短等待时间>INF,也就是无法到的,任何状态,任何乘车方法都到不了,那么就输出impossible,值得注意的是,这里的INF也相当于是一个标志*/ printf("impossible"); else printf("%d",f[0][1]); printf("\\n"); } return 0; }
以上是关于算法竞赛入门经典--例题和课后训练(动态规划)的主要内容,如果未能解决你的问题,请参考以下文章