题解:P5060 旅行
题意简述
给定一张有向图,求从点
- 经过的边的权值总和是
P 的倍数。 - 在满足条件
1 的情况下,经过的边的权值总和最小。
题目分析
本题可以使用分层图最短路来解决。
仿照动态规划的思想,定义
然后进行一遍 Dijkstra 算法,设当前考虑的节点为
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){register int t1=0,t2=0;register char x=getchar();while(x<'0' ||x>'9'){if(x=='-') t2|=1;x=getchar();}while(x>='0' && x<='9'){t1=(t1<<1)+(t1<<3)+(x^48),x=getchar();}return t2?-t1:t1;}
inline void write(int x){register int sta[35],top=0;if(x<0) putchar('-'),x=-x;do{sta[top++]=x%10,x/=10;}while(x);while(top) putchar(sta[--top]+48);}
int n,m,p,s,t,f[50005][55],pre_now[50005][55],pre_use[50005][55];
bool vis[50005][55];
struct edge{
int to,l;
};
vector<edge> v[50005];
struct node{
int now,sum,use;
};
priority_queue<node> q;
bool operator <(const node &a,const node &b){
return a.sum>b.sum;
}
vector<int> ans;
void get(int x,int y){//通过保存的路径信息得到具体地路径
if(!x) return;
ans.push_back(x);
get(pre_now[x][y],pre_use[x][y]);
}
signed main(){
n=read();
m=read();
p=read();
s=read();
t=read();
for(int i=1;i<=m;i++){
int t1=read(),t2=read(),t3=read();
v[t1].push_back({t2,t3});
}
memset(f,0x3f,sizeof(f));
f[s][0]=0;
q.push({s,0,0});
while(!q.empty()){
node temp=q.top();
q.pop();
if(vis[temp.now][temp.use]) continue;
vis[temp.now][temp.use]=1;
for(int i=0;i<v[temp.now].size();i++){
int t1=v[temp.now][i].to,t2=v[temp.now][i].l;
if(!vis[t1][(temp.use+t2)%p] && f[t1][(temp.use+t2)%p]>f[temp.now][temp.use]+t2){
f[t1][(temp.use+t2)%p]=f[temp.now][temp.use]+t2;
pre_now[t1][(temp.use+t2)%p]=temp.now;//保存路径信息
pre_use[t1][(temp.use+t2)%p]=temp.use;//同上
q.push({t1,f[t1][(temp.use+t2)%p],(temp.use+t2)%p});
}
}
}
if(f[t][0]==4557430888798830399) puts("jjc fails in travelling");//考虑无法完成的情况
else{
write(f[t][0]);
putchar('\n');
get(t,0);
for(int i=ans.size()-1;i>0;i--){
write(ans[i]);
putchar('-');
putchar('>');
}
write(ans[0]);
}
return 0;
}