uva 1025 A Spy in the Metro DAG上的动态规划问题

看dp真的是能挫败自信心,感觉智商高低就能在这里体现了…
感觉真的不太会用dp啊,尤其是递推式,不会写呢~~(感觉是自己太弱了)~~
然后这题也是看的紫书先,还是有点懵,看网上大神的题解,感觉跟紫书差不多。
但是我自己还是不是很理解啊,反复啃了好久终于是理解了。

    dp[i][j] 代表的是 现在在i时刻 j站 还需要等待多久时间..
    起点 0 1终点 T N
    那么知道 当 i=T,J=N时,说明已经在这个站了,那么等待时间为0,而i=T j等于其他的时候为无穷大(等待时间)
    那么现在逆推

AC代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int INF=301;
const int stationnumber=101;
bool isok[INF][stationnumber][2] ; //用来判断该时刻是不是有车
int d1[55],d2[55];
int dp[INF][stationnumber];
int main()
{
    int N;
    int kase=1;
    while( cin>>N && N ){
        memset(isok,0,sizeof(isok));
        int T;
        cin>>T;
        int t[25];
        for(int i=1;i<N;i++)
            cin>>t[i];
        // t[1]= 1-2 time t[2]=2-3 time

        int M1,M2;
        cin>>M1;
        for(int i=1;i<=M1;i++){
            cin>>d1[i];
            isok[d1[i]][1][0]=1; //第一个站,这个时间,有车
            int totaltime=d1[i];
            for(int j=1;j<N;j++){
                totaltime+=t[j];
                isok[totaltime][j+1][0]=1; // 第j+1个站,这个时间,有车
            }
        }
        cin>>M2;
        for(int i=1;i<=M2;i++){
            cin>>d2[i];
            isok[d2[i]][N][1]=1; //从第N站开过来的车,这个时间,有车
            int totaltime=d2[i];
            for(int j=N;j>1;j--){ //从 N-N-1的时间是 t[N-1] 依次类推
                totaltime+=t[j-1];
                isok[totaltime][j-1][1]=1;
            }
        }

        dp[T][N]=0; //当i=T,j=N时,已经在这个站了,等待时间为0
        for(int i=1;i<N;i++)
            dp[T][i]=INF;//  INF 代表无穷大,可以用来作为一个标志,说明这个时候不可能到站,很明显是这样的。

        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 && isok[i][j][0] && i+t[j]<=T) //这个决策是坐左边的车,如果j<N(因为在N的时候没有坐车的意义,并且i+t[j]<=T.
                	dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);
                //逆推!这个状态肯定是从坐车后那个时间点在那个车站的状态加上坐车的时间来的。
                if(j>1 && isok[i][j][1] && i+t[j-1]<=T) //这个决策是坐右边来的车。
                //同理,也是逆推!
            		dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);
            }
        }
		cout<<"Case Number "<<kase++<<": ";
		if(dp[0][1]>=INF)
			cout<<"impossible"<<endl;
		else cout<<dp[0][1]<<endl;
    }
    return 0;
}

2019.3.21补充 状态 d[i][j]表示 剩余i秒,在j站

#include <iostream>
#include <cstring>
using namespace std;
const int MAX =55;
int t[MAX];
bool isok[205][MAX][2]; //某时刻,某个站,哪个方向
int dp[205][MAX];
const int INF = 1e8;
int main()
{
    int N;
    int kase =1;
    while(cin>>N && N){
        int T;
        cin>>T;
            memset(dp,0,sizeof(dp));
            memset(isok,0,sizeof(isok));
            for(int i=1;i<N;i++){
                cin>>t[i];
                // t[i]代表 t[i]->t[i+1] 或者说 t[i+1]->t[i];
            }
            int M1,M2;
            cin>>M1;
            int x;
            for(int i=1;i<=M1;i++){
                cin>>x;
                int sum=x;
                isok[T-x][1][0] = 1;
                for(int j=1;j<N;j++){
                    sum+=t[j]; //时间
                    if(T-sum>=0)
                    isok[T-sum][j+1][0] = 1; //往右开的车
                }
            }
            cin>>M2;
            for(int i=1;i<=M2;i++){
                cin>>x;
                int sum=x;
                isok[T-x][N][1] = 1; // 往左开的车
                for(int j=N;j>=1;j--){
                    sum+=t[j-1];
                    if(T-sum>=0)
                    isok[T-sum][j-1][1] = 1;
                }
            }
            for(int i=1;i<N;i++) dp[0][i] = INF; //记 dp[i][j]为剩余i时间,在j站
            for(int i = 1;i<=T;i++){
                for(int j=1;j<=N;j++){

                    dp[i][j] = dp[i-1][j]+1; //等1s; 是从前一秒的状态过来的

                    if(j-1>0 && i-t[j-1]>=0 && isok[i][j][1]) //这个点可能是从右边来的车(j-1)站的时候坐过来的
                        dp[i][j] = min(dp[i][j],dp[i-t[j-1]][j-1]);

                    if(j+1<=N && i-t[j]>=0 && isok[i][j][0] )
                        dp[i][j] = min(dp[i][j],dp[i-t[j]][j+1]);
                }
            }
            cout<<"Case Number "<<kase++<<": ";
            if(dp[T][1]<INF)
                cout<<dp[T][1]<<endl;
            else cout<<"impossible"<<endl;
        }

    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值