美文网首页动态规划动态规划
城市里的间谍(dp)

城市里的间谍(dp)

作者: laochonger | 来源:发表于2018-03-30 10:33 被阅读0次

    对于这个问题,用逆推法,因为逆推了所有的时间与该时间下的站台,所以这样对于所有的状态的前一个状态都是确定的。其实对于这个问题我还是有一点疑惑,但我认为动规的问题可以跟dfs一样,看做一个抽象的问题:逻辑+遍历。

    至于最后>=INF, 等于的原因应该是在逆推过程中已经出现了无法逆推的状态
    大于的话,应该是无法逆推的状态为最后一个

    转自:https://blog.csdn.net/NOIAu/article/details/71517440

    累加器传送门:

    http://blog.csdn.net/NOIAu/article/details/71775000

    题目传送门:

    https://vjudge.net/problem/UVA-1025

    题目内容:

    Secret agent Maria was sent to Algorithms City to carry out an especially dangerous mission. After
    several thrilling events we nd her in the rst station of Algorithms City Metro, examining the time
    table. The Algorithms City Metro consists of a single line with trains running both ways, so its time
    table is not complicated.
    Maria has an appointment with a local spy at the last station of Algorithms City Metro. Maria
    knows that a powerful organization is after her. She also knows that while waiting at a station, she is
    at great risk of being caught. To hide in a running train is much safer, so she decides to stay in running
    trains as much as possible, even if this means traveling backward and forward. Maria needs to know
    a schedule with minimal waiting time at the stations that gets her to the last station in time for her
    appointment. You must write a program that nds the total waiting time in a best schedule for Maria.
    The Algorithms City Metro system has N stations, consecutively numbered from 1 to N. Trains
    move in both directions: from the rst station to the last station and from the last station back to the
    rst station. The time required for a train to travel between two consecutive stations is xed since all
    trains move at the same speed. Trains make a very short stop at each station, which you can ignore
    for simplicity. Since she is a very fast agent, Maria can always change trains at a station even if the
    trains involved stop in that station at the same time.

    输入:

    The input le contains several test cases. Each test case consists of seven lines with information as
    follows.
    Line 1. The integer N (2 � N � 50), which is the number of stations.
    Line 2. The integer T (0 � T � 200), which is the time of the appointment.
    Line 3. N 1 integers: t1; t2; : : : ; tN1 (1 � ti � 20), representing the travel times for the trains
    between two consecutive stations: t1 represents the travel time between the rst two stations, t2
    the time between the second and the third station, and so on.
    Line 4. The integer M1 (1 � M1 � 50), representing the number of trains departing from the rst
    station.
    Line 5. M1 integers: d1; d2; : : : ; dM1 (0 � di � 250 and di < di+1), representing the times at which
    trains depart from the rst station.
    Line 6. The integer M2 (1 � M2 � 50), representing the number of trains departing from the N-th
    station.
    Line 7. M2 integers: e1; e2; : : : ; eM2 (0 � ei � 250 and ei < ei+1) representing the times at which
    trains depart from the N-th station.
    The last case is followed by a line containing a single zero.

    输出:

    For each test case, print a line containing the case number (starting with 1) and an integer representing
    the total waiting time in the stations for a best schedule, or the word `impossible’ in case Maria is
    unable to make the appointment. Use the format of the sample output.

    题的核心是逆推的思想

    这道题一看要问最少需要等待多少时间,自然会想到用dp,那么怎么来处理这个问题呢?我们可以用dp[i][j]来表示时刻i,你现在身处第j个站,最少还需要等待多长时间,我们所知的是,在T时刻,你人一定需要在第n个车站去完成间谍任务,所以dp[T][n]=0;可以由这个位置来进行反推,比如我在时刻T-1可以仍然位于车站n或者说如果有往左开的车我就有选择往左坐车的可能;
    那么我们对于一个dp[i][j]进行状态转移,有三种转移方式:

    决策一:

    是我呆在原地不动,这个时候需要等的时间是由dp[i+1][j]转移而来,需要在dp[i+1][j]的基础上+1;因为我在j+1的时候的最优等待值的基础上,等待一个时间(时间从后往前逆推,注意dp数组表示的是最少还需要等待多少时间)

    dp[i][j]=dp[i+1][j]+1;
    

    决策二:

    往右移动,如果往右有车的话
    那么dp[i][j]就需要从dp[i+t[j]][j+1]转移过来
    dp[i+t[j]][j+1]表示的是时刻i+t[j]的时候(相当于是往右到下一站j+1站的时候的最优值),由于是逆推的,所以dp[i+t[j]][j+1]的值先于dp[i][j]计算出,所以dp转移合理

    dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);
    

    决策三:

    往左移动,如果往左有车的话
    那么dp[i][j]就需要从dp[i+t[j-1]][j-1]转移过来,原理和上面一样,往左需要花t[j-1]的时间,i+t[j-1]的时刻,位置在左一站的j-1站时的最优值,由于循环里先循环的是i,而且是逆向循环,所以dp[i+t[j-1]][j-1]的值也先已经计算得出,所以dp转移合理

    dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);
    

    那么如何判定在某一个时刻有没有车往左或者往右已进行dp转移呢,我们可以先进行预处理,用一个has_a _train[i][j][2]数组来存储,其中has_a _train[i][j][0]表示i时刻在j车站,有没有一辆向左开的地铁,has_a _train[i][j][1]则表示i时刻在j车站,有没有一辆向右开的地铁
    预处理和输出的代码如下:

    bool init(){
        memset(has_a_train,false,sizeof(has_a_train));
        scanf("%d",&n);
        if(n==0) return false;
        scanf("%d",&T);
        for(int i=1;i<=n-1;i++) scanf("%d",&t[i]);
        scanf("%d",&M1);
        for(int i=1;i<=M1;i++){
            scanf("%d",&dl[i]);
            int timer=dl[i];
            if(timer<=T) has_a_train[timer][1][1]=true;
            for(int i=1;i<=n-1;i++){
                if(timer+t[i]<=T){
                    has_a_train[timer+t[i]][i+1][1]=true;
                    timer+=t[i];
                }
                else break;
            }
        }
        scanf("%d",&M2);
        for(int i=1;i<=M2;i++){
            scanf("%d",&dr[i]);
            int timer=dr[i];
            if(dr[i]<=T) has_a_train[timer][n][0]=true;
            for(int i=n-1;i>=1;i--){
                if(timer+t[i]<=T){
                    has_a_train[timer+t[i]][i][0]=true;
                    timer+=t[i];
                }
                else break;
            }
        }
        return true;
    }
    

    进行预处理之后我们就可以进行O(1)的dp转移了,这样会变得非常方便,具体的dp转移是这样的

    void dpp(){
        for(int i=1;i<=n-1;i++) dp[T][i]=MAXNN;
        dp[T][n]=0;
        for(int i=T-1;i>=0;i--){
            for(int j=1;j<=n;j++){
                dp[i][j]=dp[i+1][j]+1;
                if(j<n&&has_a_train[i][j][1]&&i+t[j]<=T)
                dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);
                if(j>1&&has_a_train[i][j][0]&&i+t[j-1]<=T)
                dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);
            }
        }
    }
    

    所以到这里应该已经完全明白了这道题
    现在贴上完整代码

    #include<cstdio>
    #include<queue>
    #include<cstring>
    using namespace std;
    const int maxn = 1000;
    const int INF = 999999999;
    int dp[300][100];
    int has_train[300][100][2];
    int d_r[100];//往右 
    int d_l[100];
    int t[100];
    int n,T,m_r, m_l,cnt = 0;
    
    void init(){
        memset(has_train, 0 , sizeof(has_train));
        scanf("%d", &T);
        //scanf("%d", &t[0]);
        for(int i = 1; i < n; i++) scanf("%d", &t[i]);
        scanf("%d", &m_r);
        for(int i = 1; i <= m_r; i++){
            scanf("%d", &d_r[i]);
            int ti = d_r[i];
            if(ti <= T) has_train[ti][1][0] = 1;
            for(int j = 1; j < n; j++){
                ti += t[j];
                if(ti <= T){
                    has_train[ti][j+1][0] = 1;
                }else{
                    break;
                }
            } 
        }
        scanf("%d", &m_l);
        for(int i = 1; i <= m_l; i++){
            scanf("%d", &d_l[i]);
            int ti = d_l[i];
            if(ti <= T) has_train[ti][n][1] = 1;
            for(int j = n-1; j >= 1; j--){
                ti += t[j];
                if(ti <= T){
                    has_train[ti][j][1] = 1;
                }else{
                    break;
                }
            }
        }
        
        
    }
    
    void print(){
        cnt++;
        printf("Case Number %d: ",cnt);
        if(dp[0][1]>=INF) printf("impossible\n");
        else printf("%d\n",dp[0][1]);
    }
    
    int main(){
        //cnt = 0;
        while(scanf("%d", &n) == 1 && n ){
            init();
            for(int i = 1; i <= n-1; i++) dp[T][i] = INF;
            dp[T][n] = 0;
            
            for(int i = T-1; i >= 0; i--)
                for(int j = 1; j <= n; j++){
                    dp[i][j] = dp[i+1][j] + 1; //wait 1
                    if(j < n && has_train[i][j][0]&& i + t[j] <= T){
                        dp[i][j] =min(dp[i][j] , dp[i+t[j]][j+1]); //right
                    }
                    if(j > 1 && has_train[i][j][1] && i +t[j-1] <= T){
                        dp[i][j] =min(dp[i][j] , dp[i+t[j-1]][j-1]);//left
                    } 
                } 
            print();
        }
        return 0;
    }
    

    相关文章

      网友评论

        本文标题:城市里的间谍(dp)

        本文链接:https://www.haomeiwen.com/subject/wdibcftx.html