【问题描述】
JY是一个爱旅游的探险家,也是一名强迫症患者。
现在JY想要在C国进行一次长途旅行,C国拥有n个城市(编号为0,1,2…,n - 1),城市之间有m条道路,可能某个城市到自己有一条道路,也有可能两个城市之间有多条道路,通过每条道路都要花费一些时间。JY从0号城市开始出发,目的地为n–1号城市。由于JY想要好好参观一下C国,所以JY想要旅行恰好T小时。为了让自己的旅行更有意思,JY决定不在任何一个时刻停留(走一条到城市自己的路并不算停留)。
JY想知道是否能够花恰好T小时到达n–1号城市(每个城市可经过多次)。现在这个问题交给了你。若可以恰好到达输出“Possible”否则输出“Impossible”。(不含引号)。
【输入格式】
第一行一个正整数Case,表示数据组数。
每组数据第一行3个整数,分别为n, m, T。
接下来m行,每行3个整数x, y, z,代表城市x和城市y之间有一条耗时为z的双向边。
【输出格式】
对于每组数据输出”Possible”或者”Impossible”。
【输入样例】
2
3 3 11
0 2 7
0 1 6
1 2 5
2 1 10000
1 0 1
【输出样例】
Possible Impossible
【样例解释】
第1组:0 -> 1 -> 2 :11 第2组:显然偶数时间都是不可能的。
【数据范围】
30%的数据满足:T≤10000
另有30%的数据满足:2≤n≤5 1≤m≤100
100%的数据满足: 2≤n≤50 1≤m≤100 1≤z≤10000 1≤T≤10^18
1≤Case≤5
看了网上很多题解,并不是很明白,然后下定决心自己搞懂之后,决定写一个仍旧不是很明白的题解,重要的是自己思考。
作为一个图论问题,首先要自己想一下自己会什么,比较明显的是这不是欧拉回路或者说网络流,那么就只有SPFA了
(什么逻辑啊!!)
T比较大,所以说我们要想办法压缩,利用一个dp的思想—重叠子问题,意思是说第j时刻到达i点可能很很多种到达方式,如果我们能不总是重复计算,只需要到达了一次就不再计算,就达到了大量化简的目的(开玩笑这种题目不知道浪费了多少)。
首先直接给出思路:找出一个比较小的环(一般来说是自环或者0~1的来回路径),我们可以在这个环上走很多次才开始继续走,为什么这样可以达到压缩的目的呢,一个是比较数字化的等下直接从方程看待,另一个就是因为之前说过了到达的方式有很多种,说不定这就是一种,而且可能性很大!。
设这个环的大小为mod,则T=t+k*mod,当k=0的时候,一定不影响结果,所以可以这么操作。
压缩后d[i][j]表示在j时刻到达i点已经走过了的最小时间,这里的值%mod一定为j,比如mod=5,d[i][2]=12,就表示第一次走到i时间是2+k*mod的时候是12(k=2),因此接下来17、22、27…都可以通过不停地走那个环来达到。
最后来看答案d[n-1][t%mod],如果它的答案是t,说明刚好可以走到,如果是t-k*mod,那么就走几个环,所以只要d[n-1][t%mod]<=t就可以达到
至于方程的转移,实际上就是最短路问题,用SPFA,化简之处如例子:用一个状态就表达了17、22、27…能化简得少么?
注意:出于个人习惯,代码中所有的点的标号都被+1
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const LL inf=1e18+5;
const int maxn=55,maxm=105,maxw=10000*2+5;
struct edge
{
int to,next,w;
}E[maxm<<1];
int Case,mod;
int n,m,np,first[maxn];
LL t;
LL d[maxn][maxw];
void add(int u,int v,int w)
{
E[++np]=(edge){v,first[u],w};
first[u]=np;
}
void Init()
{
scanf("%d%d",&n,&m);
cin>>t;
mod=1e9;
np=0;
memset(first,0,sizeof(first));
int u1,u2,w;
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u1,&u2,&w);
u1++,u2++;
if(u1==1||u2==1)
{
mod=min(mod,2*w);
}
add(u1,u2,w);
add(u2,u1,w);
}
}
struct data
{
int i,k;
};
bool inq[maxn][maxw];
void SPFA()
{
queueq;
for(int i=0;i<=n;i++)
for(int j=0;j<=mod;j++)
d[i][j]=inf;
q.push((data){1,0});
d[1][0]=0;
while(!q.empty())
{
data i=q.front();q.pop();
inq[i.i][i.k]=0;
for(int p=first[i.i];p;p=E[p].next)
{
int j=E[p].to,nk=(i.k+E[p].w)%mod;
if(d[j][nk]>d[i.i][i.k]+E[p].w)
{
d[j][nk]=d[i.i][i.k]+E[p].w;
if(inq[j][nk])continue;
inq[j][nk]=1;
q.push((data){j,nk});
}
}
}
}
void solve()
{
if(mod==1e9)
{
printf("Impossible\n");
return;
}
SPFA();
if(d[n][t%mod]<=t)
printf("Possible\n");
else
printf("Impossible\n");
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d",&Case);
while(Case--)
{
Init();
solve();
}
return 0;
}
压缩问题可以从冲突上分析究竟获得了多少化简,同样的也可以通过冲突来得到压缩算法