题目描述
JYJY是一个爱旅游的探险家,也是一名强迫症患者。现在JYJY想要在CC国进行一次长途旅行,C国拥有n个城市(编号为),城市之间有mm条道路,可能某个城市到自己有一条道路,也有可能两个城市之间有多条道路,通过每条道路都要花费一些时间。从0号城市开始出发,目的地为n–1n–1号城市。由于JYJY想要好好参观一下C国,所以JYJY想要旅行恰好TT小时。为了让自己的旅行更有意思,决定不在任何一个时刻停留(走一条到城市自己的路并不算停留)。JYJY想知道是否能够花恰好T小时到达n−1n−1号城市(每个城市可经过多次)。现在这个问题交给了你。
若可以恰好到达输出“PossiblePossible”否则输出“ImpossibleImpossible”。(不含引号)。
输入格式
第一行一个正整数CaseCase,表示数据组数。
每组数据第一行33个整数,分别为。
接下来mm行,每行个整数x,y,zx,y,z,代表城市xx和城市之间有一条耗时为zz的双向边。
输出格式
对于每组数据输出””或者”ImpossibleImpossible”.
数据范围
30%: T≤10000T≤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≤1018,Case≤5.
Solutiuon
前言
之所以拿出这道题,因为这是一道非常好的模板题。
30opt
30opt30opt算是容易想到。用dis[i][j]dis[i][j]表示到达ii位置用了时间。实际上就是对一个点分成很多不同的状态,然后把一个状态看成一个点加入队列用SPFA的思想dpdp看是否能到达就可以了。
100opt
注意到TT值巨大,不得不优化一下算法。我们发现对于来说,到达ii的时间一定是j.显然有大部分状态没有用。我们发现如果到达i时有这样两个时间和j+kj+k,这两种时间显然都是合法的,也就是有一种走法可以实现的 ,那么如果这个k值有特殊意义的话,那么此题很好办。如果这个kk值可以在任何一种走法中加入若干次,那么如果到达一个点存在一种走法使得到这点的时间为,那么一定有走法使得到这点时间为j+k,j+2k,j+3k,j+4k,......j+k,j+2k,j+3k,j+4k,......,因为kk可以任意加入,那么,这样的是什么?
k一定可以是从起点出发的,然后回到起点的一个环的长度。这样的合法性显然。
清楚了这个,如果dis[i][j]dis[i][j]合法,那么dis[i][j+n×k],n∈N∗dis[i][j+n×k],n∈N∗合法。
那么这些就没有必要存储了。进而推知,只需存储dis[i][jmodk]dis[i][jmodk]的状态。这样相当于把之前所有的jj都转化成了。那么这样的转移会有错误吗?实际上由于取模运算具有较强的灵活性,(j1+n×k+j2+p×k)(j1+n×k+j2+p×k)整体取模kk与取模k是一样的,所以转移式
仔细研究转移式,dp[i][j]dp[i][j]的值一定可以写成j+k×nj+k×n的形式,所以dp[i][j]dp[i][j]的值越小越优,越能证明他能达到比他大n×kn×k的时间.所以此处需套用最短路的模板.
接下来,判断一下能否在终点到达TT时刻.我们知道如果能够,那么一定有值,这个值又可以写成(Tmodk)+k×p1,p1∈N∗(Tmodk)+k×p1,p1∈N∗,TT也可以写成,如果这个值小于T,也就是p1≤p2p1≤p2那么一定可以在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;
}

本文介绍了一种解决特定旅行问题的有效算法。该问题要求在给定的城市和道路网络中找到一个方案,使得从起始城市出发,经过指定小时数后精确到达目标城市。文章详细讨论了如何通过状态压缩和优化策略来简化问题,并给出了具体的实现代码。
517

被折叠的 条评论
为什么被折叠?



