Description
众所周知,香港记者跑得比谁都快,这其中其实是有秘诀的。
首先他们会跑最短路,但是最短路会有很多条,而且其他记者也知道要跑最短路,香港记者还统计出了每座城市带黑框眼镜的人数,如果一个记者跑路的时候城市带黑框眼镜人数的序列字典序比另一个记者大,那么这个记者就会被不可描述的力量续走时间,导致他跑得没字典序小的记者快。
长者日续万秒日理万机,想请你告诉他香港记者经过的总路程和城市带黑框眼镜人数的序列,方便他找到香港记者,传授他们一些人生经验。
方便起见,设起点为 1 终点为 n。
由于续命的多样性不可描述的力量,各个城市带黑框眼镜的人数各不相同。
Input
输入文件名为 journalist.in。
第一行,两个整数 n; m 表示有 n 个城市,城市之间有 m 条有向边。
第二行, n 个数,表示每个城市带黑框眼镜的人数 bi:
接下来 m 行,每行 3 个非负整数 ui; vi; wi 表示一条有向边的起点,终点,路程。
Output
输出文件名为 journalist.out。
第一行,一个非负整数表示香港记者经过的总路程。
第二行,若干个非负整数表示香港记者经过的城市带黑框眼镜人数的序列。
Sample Input
8 9
1 2 3 4 5 6 7 8
1 2 2
2 3 3
3 8 3
1 4 3
4 5 2
5 8 1
1 6 1
6 7 2
7 8 3
Sample Output
6
1 4 5 8
Hint
对于前 30% 的数据, 2 ≤ n ≤ 2 × 103, 1 ≤ m ≤ 4 × 103。
对于前 60% 的数据,保证数据随机。
对于另外 30% 的数据,保证所有起点到终点的简单路径(没有环的路径)长度相同。
对于 100% 的数据, 2 ≤ n ≤ 2 × 105, 1 ≤ m ≤ 4 × 105, 1 ≤ w ≤ 1 × 109,存在至少一条从
起点到终点的最短路。
题解
解法一:
我们可以先求出最短路。
然后搜索求出路径。
对于u−>v,如果
dist[v]==dist[u]+w(u,v)
就继续拓展。
我们可以加边的时候先将后继节点按点权排序,这样贪心保证搜出的第一条策略就是满足条件的。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> using namespace std; typedef long long lol; template <typename T> void read(T &x) { x=0;char c=getchar(); for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar())x=x*10+c-‘0‘; } int n,m,s[200005]; struct Edge { int from,to;lol dis; }map[400005]; int size=0,head[200005]; struct node { int next,to; lol dis; }edge[400005]; void putin(int from,int to,lol dis) { size++; edge[size].next=head[from]; edge[size].to=to; edge[size].dis=dis; head[from]=size; } bool cmp(const Edge a,const Edge b) { if(a.from!=b.from)return a.from<b.from; if(a.to!=b.to)return s[a.to]>s[b.to]; else return a.dis<b.dis; } lol dist[200005]; int vis[200005],pre[200005]; void SPFA(int r) { memset(dist,127/3,sizeof(dist)); queue<int>mem; mem.push(r); vis[r]=1; dist[r]=0; while(!mem.empty()) { int x=mem.front();mem.pop(); vis[x]=0; for(int i=head[x];i!=-1;i=edge[i].next) { int y=edge[i].to; if(dist[y]>dist[x]+edge[i].dis) { dist[y]=dist[x]+edge[i].dis; if(!vis[y]) { mem.push(y); vis[y]=1; } } } } return; } bool ok; void dfs(int r) { if(r==n)ok=1; if(ok)return; int i; for(i=head[r];i!=-1;i=edge[i].next) { int y=edge[i].to; if(dist[y]==dist[r]+edge[i].dis) { pre[y]=r; dfs(y); } } } int ans[200005],cnt; void write(int r) { while(r!=1) { ans[++cnt]=s[r]; r=pre[r]; } ans[++cnt]=s[r]; for(int i=cnt;i>=2;i--)printf("%d ",ans[i]); printf("%d",ans[1]); return; } int main() { freopen("journalist.in","r",stdin); freopen("journalist.out","w",stdout); int i,j; read(n);read(m); memset(head,-1,sizeof(head)); for(i=1;i<=n;i++)read(s[i]); for(i=1;i<=m;i++) { read(map[i].from); read(map[i].to); read(map[i].dis); } sort(map+1,map+m+1,cmp); for(i=1;i<=m;i++)putin(map[i].from,map[i].to,map[i].dis); SPFA(1); printf("%lld\n",dist[n]); dfs(1); write(n); return 0; }
解法二:
我们可以存储逆边,逆向做一次SPFA。
pre[u]表示u的后继节点
松弛的时候如果
dist[v]>dist[u]+w(u,v)
就
dist[v]=dist[u]+w(u,v);
如果
dist[v]==dist[u]+w(u,v)
此时若pre[v]的点权大于u的点权,我们将
pre[v]=u;
由于字典序高位越小越好,满足最优子结构,贪心是可行的。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<queue> using namespace std; typedef long long lol; template <typename T> void read(T &x) { x=0;char c=getchar(); for(;!isdigit(c);c=getchar()); for(;isdigit(c);c=getchar())x=x*10+c-‘0‘; } int n,m,size=0,head[200005],s[200005]; struct node { int next,to; lol dis; }edge[400005]; void putin(int from,int to,lol dis) { size++; edge[size].next=head[from]; edge[size].to=to; edge[size].dis=dis; head[from]=size; } lol dist[200005]; int vis[200005],pre[200005]; void SPFA(int r) { memset(dist,127/3,sizeof(dist)); memset(pre,127/3,sizeof(pre)); queue<int>mem; mem.push(r); vis[r]=1; dist[r]=0; while(!mem.empty()) { int x=mem.front();mem.pop(); vis[x]=0; for(int i=head[x];i!=-1;i=edge[i].next) { int y=edge[i].to; if(dist[y]==dist[x]+edge[i].dis&&s[pre[y]]>s[x])pre[y]=x; if(dist[y]>dist[x]+edge[i].dis) { dist[y]=dist[x]+edge[i].dis; pre[y]=x; if(!vis[y]) { mem.push(y); vis[y]=1; } } } } return; } void write(int r) { while(r!=n) { printf("%d ",s[r]); r=pre[r]; } printf("%d",s[r]); return; } int main() { freopen("journalist.in","r",stdin); freopen("journalist.out","w",stdout); int i,j; memset(head,-1,sizeof(head)); read(n);read(m); for(i=1;i<=n;i++) read(s[i]); for(i=1;i<=m;i++) { int from,to,dis; read(from);read(to);read(dis); putin(to,from,dis); } SPFA(n); printf("%lld\n",dist[1]); write(1); return 0; }