[校内测试]旅行(最短路)

=== ===

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

=== ===

题解

可以想到这个人的状态只跟两个东西有关——它在哪辆车上和他当前所在的位置。其实一开始想的是DP结果后来写着写着就写成了最短路。。实际上这题就是个最短路,相当于拆点。这里直接用结构体记录它的状态。然而等来的车和等回的车是不一样的,所以要把每辆车拆成两个分别对应两趟。对于每个待扩展的点,它有两种选择——坐当前这个车往下个点走和换一个车。然而如果这个点刚换过车还没走的话它就不用再换一遍了。所以在每个点里面还得带一个标记表示它是不是刚换过车。

如果这个点要换车的话,就要枚举所有经过这个点的车,可以存到一个邻接表里。然后要计算如果这个人等这个车的话它要等多久,这个地方是这个题最难绕出来的一个地方了。。这里ATP使用的过程带了三个参数,一个是当前的分钟数,计算方法是用出发时间加上已经过去的分钟数;一个是选择的这趟车走过来需要多长时间,这个也是预处理的;还有一个表示的是这个车的频率。首先用目前的分钟数减去这趟车走过来的时间得到一个分钟数T,这个分钟数表示的含义是如果这趟车在每个小时的第T分钟发车,这个人就一分钟也不用等。以这个T为基准来计算它要等待多长时间,注意一定要保证T为正数,因为减法的时候可能减出负数来,要模一下60把它搞成正的。

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k,x,y,g,m,tt[4010][1010],c[4010],p[1010],a[10010],next[10010],nst[4010][1010];
int stn[1010],r[1010],wst[4010][1010],tot;
bool ext[4010][1010];
struct point{
    int trn,pos,dis;
    bool chg;
    bool operator < (const point &a)const
    {return dis>a.dis;}
}S;
priority_queue<point> q;
void add(int ston,int trin){
    tot++;a[tot]=trin;next[tot]=p[ston];p[ston]=tot;
}
int gettime(int nowtime,int prev,int tc){
    int wer=nowtime-prev,wt;
    while (wer>=60) wer-=60;
    while (wer<0) wer+=60;
    wt=tc-(wer%tc);
    if (wt==tc) return 0;
    else return wt;
}
int main()
{
    freopen("pod.in","r",stdin);
    freopen("pod.out","w",stdout);
    scanf("%d%d%d%d%d%d",&n,&k,&x,&y,&g,&m);
    for (int i=1;i<=k;i++){
        int s,prv=0,sum=0;
        scanf("%d%d",&s,&c[i*2]);
        c[i*2-1]=c[i*2];//i*2-1是正向,i*2是反向
        for (int j=1;j<=s;j++){
            scanf("%d",&stn[j]);
            add(stn[j],i*2-1);add(stn[j],i*2);
            if (j!=1){
                nst[i*2-1][stn[j-1]]=stn[j];
                nst[i*2][stn[j]]=stn[j-1];
            }
        }
        for (int j=1;j<s;j++){
            scanf("%d",&r[j]);
            sum+=r[j];
            wst[i*2-1][stn[j]]=r[j];
            wst[i*2][stn[j+1]]=r[j];
        }
        for (int j=1;j<s;j++){
            tt[i*2-1][stn[j]]=prv;
            tt[i*2][stn[j]]=sum-prv;
            prv+=r[j];//预处理每一趟车走到某个点需要多长时间
        }//正向车就是前缀和,反向车是总路径长度减去前缀和
        tt[i*2-1][stn[s]]=prv;
        tt[i*2][stn[s]]=sum-prv;
    }
    S.trn=S.dis=0;
    S.pos=x;S.chg=false;
    q.push(S);
    while (!q.empty()){
        point u,v;
        u=q.top();q.pop();
        if (u.pos==y){S=u;break;}
        ext[u.trn][u.pos]=true;
        if (u.chg==false){
            for (int i=p[u.pos];i!=0;i=next[i])
              if (a[i]!=u.trn){
                  int wait=gettime(m+u.dis,tt[a[i]][u.pos],c[a[i]]);
                  v.trn=a[i];v.pos=u.pos;
                  v.dis=u.dis+wait;
                  v.chg=true;
                  if (ext[v.trn][v.pos]==false) q.push(v);
              }
        }
        v.trn=u.trn;//在当前这辆车上往后走一个位置
        v.pos=nst[u.trn][u.pos];
        v.dis=u.dis+wst[u.trn][u.pos];
        v.chg=false;
        if (v.pos!=0&&ext[v.trn][v.pos]==false)//注意判断状态是否合法
          q.push(v);
    }
    m+=S.dis;
    while (m>=60){
        ++g;
        if (g==24) g=0;
        m-=60;
    }
    printf("%d %d\n",g,m);
    return 0;
}

偏偏在最后出现的补充说明

最短路这玩意儿好像跟DP有某种程度上的相似之处。。。好像把所有会影响到它路程长短的状态都表示出来就可以保证正确性了对吧。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值