题目描述
JY
J
Y
是一个爱旅游的探险家,也是一名强迫症患者。现在
JY
J
Y
想要在
C
C
国进行一次长途旅行,C国拥有n个城市(编号为),城市之间有
m
m
条道路,可能某个城市到自己有一条道路,也有可能两个城市之间有多条道路,通过每条道路都要花费一些时间。从0号城市开始出发,目的地为
n–1
n
–
1
号城市。由于
JY
J
Y
想要好好参观一下C国,所以
JY
J
Y
想要旅行恰好
T
T
小时。为了让自己的旅行更有意思,决定不在任何一个时刻停留(走一条到城市自己的路并不算停留)。
JY
J
Y
想知道是否能够花恰好T小时到达
n−1
n
−
1
号城市(每个城市可经过多次)。现在这个问题交给了你。
若可以恰好到达输出“
Possible
P
o
s
s
i
b
l
e
”否则输出“
Impossible
I
m
p
o
s
s
i
b
l
e
”。(不含引号)。
输入格式
第一行一个正整数
Case
C
a
s
e
,表示数据组数。
每组数据第一行
3
3
个整数,分别为。
接下来
m
m
行,每行个整数
x,y,z
x
,
y
,
z
,代表城市
x
x
和城市之间有一条耗时为
z
z
的双向边。
输出格式
对于每组数据输出””或者” Impossible I m p o s s i b l e ”.
数据范围
30%:
T≤10000
T
≤
10000
另有
30%:
n≤5,m≤10.
n
≤
5
,
m
≤
10.
100%:
2≤n≤50,1≤m≤100,1≤z≤10000,1≤T≤1018,Case≤5.
2
≤
n
≤
50
,
1
≤
m
≤
100
,
1
≤
z
≤
10000
,
1
≤
T
≤
10
18
,
C
a
s
e
≤
5.
Solutiuon
前言
之所以拿出这道题,因为这是一道非常好的模板题。
30opt
30opt 30 o p t 算是容易想到。用 dis[i][j] d i s [ i ] [ j ] 表示到达 i i 位置用了时间。实际上就是对一个点分成很多不同的状态,然后把一个状态看成一个点加入队列用SPFA的思想 dp d p 看是否能到达就可以了。
100opt
注意到
T
T
值巨大,不得不优化一下算法。我们发现对于来说,到达
i
i
的时间一定是j.显然有大部分状态没有用。我们发现如果到达i时有这样两个时间和
j+k
j
+
k
,这两种时间显然都是合法的,也就是有一种走法可以实现的 ,那么如果这个k值有特殊意义的话,那么此题很好办。如果这个
k
k
值可以在任何一种走法中加入若干次,那么如果到达一个点存在一种走法使得到这点的时间为,那么一定有走法使得到这点时间为
j+k,j+2k,j+3k,j+4k,......
j
+
k
,
j
+
2
k
,
j
+
3
k
,
j
+
4
k
,
.
.
.
.
.
.
,因为
k
k
可以任意加入,那么,这样的是什么?
k一定可以是从起点出发的,然后回到起点的一个环的长度。这样的合法性显然。
清楚了这个,如果
dis[i][j]
d
i
s
[
i
]
[
j
]
合法,那么
dis[i][j+n×k],n∈N∗
d
i
s
[
i
]
[
j
+
n
×
k
]
,
n
∈
N
∗
合法。
那么这些就没有必要存储了。进而推知,只需存储
dis[i][jmodk]
d
i
s
[
i
]
[
j
mod
k
]
的状态。这样相当于把之前所有的
j
j
都转化成了。那么这样的转移会有错误吗?实际上由于取模运算具有较强的灵活性,
(j1+n×k+j2+p×k)
(
j
1
+
n
×
k
+
j
2
+
p
×
k
)
整体取模
k
k
与取模k是一样的,所以转移式
仔细研究转移式, dp[i][j] d p [ i ] [ j ] 的值一定可以写成 j+k×n j + k × n 的形式,所以 dp[i][j] d p [ i ] [ j ] 的值越小越优,越能证明他能达到比他大 n×k n × k 的时间.所以此处需套用最短路的模板.
接下来,判断一下能否在终点到达 T T 时刻.我们知道如果能够,那么一定有值,这个值又可以写成 (Tmodk)+k×p1,p1∈N∗ ( T mod k ) + k × p 1 , p 1 ∈ N ∗ , T T 也可以写成,如果这个值小于T,也就是 p1≤p2 p 1 ≤ p 2 那么一定可以在T时刻走到n点.
总结一下
实际上就是一个压缩状态的问题
代码
特别鸣谢YSN巨佬提供AC代码
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#define ll long long
#define re register
#define il inline
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=500,mod=10000;
int T,n,m,cnt,h[N],f=0,ysn,d;
ll t,dp[20005][105];
bool vis[20005][105];
struct Edge{int to,nxt,w;}e[N<<1];
il void add(re int u,re int v,re int w){e[++cnt]=(Edge){v,h[u],w};h[u]=cnt;}
il ll gi()
{
re ll x=0,t=1;
re char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il void wri(re int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) wri(x/10);
putchar(x%10+'0');
}
struct node{int u,len,w;bool operator < (const node &o) const {return w>o.w;}};
priority_queue<node>Q;
il void SPFA()
{
memset(dp,63,sizeof(dp));memset(vis,0,sizeof(vis));
Q.push((node){1,0,0});dp[0][1]=0;
while(!Q.empty())
{
re node now=Q.top();Q.pop();re int u=now.u,len=now.len%d,w=now.w;
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to,lenn=(len+e[i].w)%d;
if(dp[lenn][v]>dp[len][u]+e[i].w)
{
dp[lenn][v]=dp[len][u]+e[i].w;
if(!vis[lenn][v]) vis[lenn][v]=1,Q.push((node){v,lenn,dp[lenn][v]});
}
}
vis[len][u]=0;
}
}
int main()
{
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
T=gi();
while(T--)
{
n=gi();m=gi();t=gi();
memset(h,-1,sizeof(h));cnt=0;
fp(i,1,m)
{
re int u=gi()+1,v=gi()+1,w=gi();
add(u,v,w);add(v,u,w);
}
d=1e9;
for(re int i=h[1];i+1;i=e[i].nxt)
{
re int v=e[i].to;
if(v^1) d=min(d,e[i].w);
}
d<<=1;
//d=1;
SPFA();
puts(dp[t%d][n]<=t?"Possible":"Impossible");
}
fclose(stdin);
fclose(stdout);
return 0;
}