题目大意
给你一个n个点m条边的无向图,每条边有正整数的边权,问是否存在一条0到n-1的长度为T的路径(点和边可以重复)。
n,m≤50 边权不超过10000 T≤1018
数据组数不超过3
分析
这道题有点考思维啊!
考虑这样的一条路径。如果不是简单路径,它可能会包括若干个环。
如果我确定了一个必须走的环,假设它的长度为len,那么可以这样设状态:
f[i][j][0/1]表示:走到了i这个点,路径的长度模len等于j,0/1表示我是否走过这个环。f[i][j][k]记录的是满足i,j,k的最短路径长度。然后答案就是判断f[n-1][T%len][1]是否小于等于T。
证明上面的算法的正确性:假设我在路径上有另一个环,那么假设第一次走进这个环是路径长度模len为j,走这个环若干次后,第二维状态一定会回到j。多个环也类似。
所以剩下的就是枚举可能的环了。发现单独一条边也可以构造出一个环,而且边权不是很大,所以可以考虑枚举所有边。同时,修改第三维01状态表示这条边是否经过(即不一定要走环),正确性没有变。这样就可以通过所有数据了。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=55,M=20005;
typedef long long LL;
LL Len,f[N][M][2];
int Case,n,m,h[N],e[N*2],nxt[N*2],E[N][3],tot,D[N*2],Data[N*M][3];
bool v[N][M][2];
void add(int x,int y,int len)
{
e[++tot]=y; nxt[tot]=h[x]; h[x]=tot; D[tot]=len;
}
bool check(int p)
{
int mo=E[p][2]*2;
memset(v,0,sizeof(v));
for (int i=0;i<n;i++) for (int j=0;j<mo;j++) f[i][j][0]=f[i][j][1]=Len+1;
Data[tot=1][0]=Data[1][1]=Data[1][2]=0;
f[0][0][0]=0;
for (int i=0;i!=tot;)
{
i=i%(N*M)+1;
int x=Data[i][0],y=Data[i][1],z=Data[i][2];
for (int j=h[x];j;j=nxt[j])
{
int l=(y+D[j])%mo,
bz=((x==E[p][0] && e[j]==E[p][1] || x==E[p][1] && e[j]==E[p][0])&&D[j]==E[p][2])?1:z;
if (f[x][y][z]+D[j]<f[e[j]][l][bz])
{
f[e[j]][l][bz]=f[x][y][z]+D[j];
if (!v[e[j]][l][bz])
{
v[e[j]][l][bz]=1;
tot=tot%(N*M)+1;
Data[tot][0]=e[j]; Data[tot][1]=l; Data[tot][2]=1;
}
}
}
v[x][y][z]=0;
}
return f[n-1][Len%mo][1]<=Len;
}
void work()
{
memset(h,0,sizeof(h));
tot=0;
scanf("%d%d%lld",&n,&m,&Len);
for (int i=0;i<m;i++)
{
scanf("%d%d%d",&E[i][0],&E[i][1],&E[i][2]);
add(E[i][0],E[i][1],E[i][2]); add(E[i][1],E[i][0],E[i][2]);
}
for (int i=0;i<m;i++)
if (check(i))
{
printf("Possible\n"); return;
}
printf("Impossible\n");
}
int main()
{
for (scanf("%d",&Case);Case--;work());
return 0;
}