【51nod1326】遥远的旅途

55 篇文章 0 订阅
18 篇文章 0 订阅

Description

一个国家有N个城市,这些城市被标为0,1,2,…N-1。这些城市间连有M条道路,每条道路连接两个不同的城市,且道路都是双向的。一个小鹿喜欢在城市间沿着道路自由的穿梭,初始时小鹿在城市0处,它最终的目的地是城市N-1处。小鹿每在一个城市,它会选择一条道路,并沿着这条路一直走到另一个城市,然后再重复上述过程。每条道路会花费小鹿不同的时间走完,在城市中小鹿不花时间逗留。路程中,小鹿可以经过一条路多次也可以经过一个城市多次。给定城市间道路的信息,问小鹿是否有一种走法,从城市0出发到达城市N-1时,恰好一共花费T个单位的时间。如果存在输出“Possible”,否则输出“Impossible”。
注意,小鹿在整个过程中可以多次经过城市N-1,只要最终小鹿停在城市N-1即可。

例如样例中小鹿的行程可以是0->1->2->0->2.

多组测试数据,输入的第一行含一个整数caseT,表示测试数据个数,1<=caseT<=3.
之后有caseT组相同结构的测试数据,每组测试数据构成如下:
第一行三个整数,N,M,T,且2<=N<=50,1<=M<=50,1<=T<=1,000,000,000,000,000,000(即10^18).
之后M行,每行三个整数Ai,Bi,Di,表示城市Ai与Bi间有一条双向道路,且小鹿穿越这条路要花费Di的时间。其中,0<=Ai,Bi< N,1<=Di<=10000。

Solution

我们想想最简单的解法。设出f[i][j]表示当前走到第i个点花费j的时间是否可行。这样空间显然承受不了。
于是我们改变一下思路。我们发现,假设从起点走到终点的有一条路径的长度为a,假设它再往一条与终点相连的长为w的路径反复走无数次后使路径长度到达了T,那么一定满足T-a==0(%2*w),即T(%2 *w)==a(%2 *w)。所以我们枚举一条与终点相连的长为w的路径,设出f[i][j]表示当前到了i点,走的距离f[i][j]最小且满足f[i][j]%(2*w)=j,做spfa即可,时间复杂度O( 40000NM )。
解释一下
有人可能会问,万一我从起点走到一半在一段上反复走,最后才一路走到终点呢?没关系,因为dp已经考虑了这种情况,即在spfa中走回自己的父亲,因为要保证f[i][j]尽量小,所以假设儿子的f[i][(j+cost[k])%2*w]由父亲f[i][j]更新,而儿子的f[i][(j+cost[k])%2*w]又可以更新父亲的f[i][(j+2*cost[k])%2*w],那这样是允许更新的。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=2e4+5,maxn1=55;
ll f[maxn1][maxn],value[maxn],s;
int bz[maxn1][maxn],next[maxn],last[maxn],first[maxn],v[maxn*maxn1*2][2];
int n,i,t,j,k,l,x,y,z,m,num;
void lian(int x,int y,int z){
    last[++num]=y;next[num]=first[x];first[x]=num;value[num]=z;
}
void spfa(int p){
    int i=0,j=1,t,k,x,y;v[1][0]=1;bz[1][0]=1;
    for (k=1;k<=n;k++)
        for (t=0;t<p;t++)
            f[k][t]=s+1;
    f[1][0]=0;
    while (i<j){
        x=v[++i][0];y=v[i][1];
        for (t=first[x];t;t=next[t]){
            if (f[x][y]+value[t]>=f[last[t]][(y+value[t])%p]) continue;
            f[last[t]][(y+value[t])%p]=f[x][y]+value[t];
            if (!bz[last[t]][(y+value[t])%p]) v[++j][0]=last[t],v[j][1]=(y+value[t])%p,bz[v[j][0]][v[j][1]]=1;
        }
        bz[x][y]=0;
    }
}
int main(){
//  freopen("data.in","r",stdin);
    scanf("%d",&l);
    while (l){l--;memset(first,0,sizeof(first));num=0;
    scanf("%d%d%lld",&n,&m,&s);
    for (i=1;i<=m;i++)
        scanf("%d%d%d",&x,&y,&z),x++,y++,lian(x,y,z),lian(y,x,z);
    k=0;
    for (t=first[n];t;t=next[t]){
        spfa(2*value[t]);
        if (f[n][s%(2*value[t])]<=s){
            printf("Possible\n");k++;break;
        }
    }
    if (!k)printf("Impossible\n");
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值