[51nod1326]遥远的旅途

Description

一张有n个点,m条变的无向图,每条边有边权。
在0时刻有一个人在点1,每一次他走过一条边,消耗的时间为这条边的边权,而不能停留在原地。
现在他想知道是否存在一种方案使得他在T时刻刚好到达点n。
多组数据,case<=3,2<=N<=50,1<=M<=50,1<=T<=10^18

Solution

上上周做GDOI组时WorldWide_D说这道题是T2原题,于是就被强行安利来搞这道题了。。。
发现真的是完全背包问题的加强版~~不过思路是一样的。
先枚举一条到n的边,我们要判断有没有一种方案,使得可以走到n之后,一直走这条边直到时刻T。
那么我们设这条边边权为w,Fi,j表示到点i时刻%(2*w)为j的最小的时刻。
那么转移显然,不过会出现环,那么就把每个状态看做一个点,跑一遍spfa就好了。
然后判断状态Fn,T%(2*w)是否小于T就好了。
小于T就表示这是个可行方案,不行的话还要继续枚举。。。
设总共有K个状态,时间复杂度理论O(NK^2)(玄学spfa)
加一个玄学剪枝优化就过了。。。(反正你去写dijkstra也没问题,复杂度有保证呵)

Code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
using namespace std;
typedef long long ll;
const int N=1e6+5;
ll dis[N],T;
int n,m,l,ty,x[N],y[N],z[N],d[N*10];
int t[N*2],next[N*2],v[N*2],last[N];
bool bz[N];
ll read() {
    char ch;while (!isdigit(ch=getchar()));
    ll o=ch-48;while (isdigit(ch=getchar())) o=o*10+ch-48;
    return o;
}
int get(int x,int y,int z) {
    return (x-1)*z+y+1;
}
void add(int x,int y,int z) {
    t[++l]=y;v[l]=z;next[l]=last[x];last[x]=l;
}
void link(int w) {
    memset(last,0,sizeof(last));l=0;
    fo(i,1,m) fo(j,0,w-1) {
        add(get(x[i],j,w),get(y[i],(j+z[i])%w,w),z[i]);
        add(get(y[i],j,w),get(x[i],(j+z[i])%w,w),z[i]);
    }
}
void spfa() {
    memset(dis,127,sizeof(dis));dis[1]=0;
    memset(bz,0,sizeof(bz));bz[1]=1;
    int i=0,j=1;d[1]=1;
    while (i<j) {
        rep(k,d[++i]) 
            if (dis[t[k]]>dis[d[i]]+(ll)v[k]) {
                dis[t[k]]=dis[d[i]]+(ll)v[k];
                if (!bz[t[k]]) {
                    bz[t[k]]=1,d[++j]=t[k];
                    if (dis[d[i+1]]>dis[d[j]]) swap(d[i+1],d[j]);
                }
            }
        bz[d[i]]=0;
    }
}
int main() {
    for(ty=read();ty;ty--) {
        n=read();m=read();T=read();bool pd=0;
        fo(i,1,m) x[i]=read(),y[i]=read(),z[i]=read(),x[i]++,y[i]++;
        fo(i,1,m) if (x[i]==n||y[i]==n) {
            link(z[i]*2);spfa();
            if (dis[get(n,T%(z[i]*2),z[i]*2)]<=T) {pd=1;break;}
        }
        if (pd) printf("Possible\n");
        else printf("Impossible\n");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值