[测试题]香港记者

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 × 1031 m 4 × 103
对于前 60% 的数据,保证数据随机。
对于另外 30% 的数据,保证所有起点到终点的简单路径(没有环的路径)长度相同。
对于 100% 的数据, 2 n 2 × 1051 m 4 × 1051 w 1 × 109,存在至少一条从
起点到终点的最短路。

题解

解法一:

我们可以先求出最短路。

然后搜索求出路径。

对于$u->v$,如果

$$dist[v]==dist[u]+w(u,v)$$

就继续拓展。

我们可以加边的时候先将后继节点按点权排序,这样贪心保证搜出的第一条策略就是满足条件的。

  1 #include<set>
  2 #include<map>
  3 #include<cmath>
  4 #include<ctime>
  5 #include<queue>
  6 #include<stack>
  7 #include<vector>
  8 #include<cstdio>
  9 #include<string>
 10 #include<cstdlib>
 11 #include<cstring>
 12 #include<iostream>
 13 #include<algorithm>
 14 #define LL long long
 15 #define RE register
 16 #define IL inline
 17 using namespace std;
 18 const int N=2e5;
 19 const int M=4e5;
 20 
 21 IL int Read()
 22 {
 23     int sum=0;
 24     char c=getchar();
 25     while (c<'0'||c>'9') c=getchar();
 26     while (c>='0'&&c<='9') sum=sum*10+c-'0',c=getchar();
 27     return sum;
 28 }
 29 
 30 int n,m,u,v,w;
 31 int b[N+5];
 32 struct ss
 33 {
 34     int u,v,w;
 35 }lin[M+5];
 36 bool comp(const ss &x,const ss &y) {return x.u==y.u ? b[x.v]>b[y.v]:x.u<y.u;}
 37 struct tt
 38 {
 39     int to,next,cost;
 40 }edge[M+5];
 41 int path[N+5],top;
 42 IL void Add(int u,int v,int w);
 43 
 44 LL dist[N+5];
 45 IL void SPFA();
 46 
 47 int keep[N+5],tot;
 48 void put_road(int r);
 49 
 50 int main()
 51 {
 52     n=Read(),m=Read();
 53     for (RE int i=1;i<=n;i++) b[i]=Read();
 54     for (RE int i=1;i<=m;i++) lin[i].u=Read(),lin[i].v=Read(),lin[i].w=Read();
 55     sort(lin+1,lin+1+n,comp);
 56     for (RE int i=1;i<=m;i++) Add(lin[i].u,lin[i].v,lin[i].w);
 57     SPFA();
 58     printf("%lld
",dist[n]);
 59     keep[++tot]=b[1];
 60     put_road(1);
 61     return 0;
 62 }
 63 
 64 IL void Add(int u,int v,int w)
 65 {
 66     edge[++top].to=v;
 67     edge[top].next=path[u];
 68     edge[top].cost=w;
 69     path[u]=top;
 70 }
 71 IL void SPFA()
 72 {
 73     memset(dist,127/3,sizeof(dist));
 74     dist[1]=0;
 75     bool vis[N+5]={0};
 76     vis[1]=1;
 77     queue<int>Q;
 78     while (!Q.empty()) Q.pop();
 79     Q.push(1);
 80     while (!Q.empty())
 81     {
 82         int u=Q.front(),v;Q.pop();vis[u]=0;
 83         for (RE int i=path[u];i;i=edge[i].next)
 84         {
 85             v=edge[i].to;
 86             if (dist[v]>dist[u]+edge[i].cost)
 87             {
 88                 dist[v]=dist[u]+edge[i].cost;
 89                 if (!vis[v])
 90                 {
 91                     vis[v]=1;
 92                     Q.push(v);
 93                 }
 94             }
 95         }
 96     }
 97     return;
 98 }
 99 
100 void put_road(int r)
101 {
102     if (r==n)
103     {
104         for (RE int i=1;i<=tot;i++) printf("%d ",keep[i]);
105         exit(0);
106     }
107     for (int i=path[r];i;i=edge[i].next) if (dist[edge[i].to]==dist[r]+edge[i].cost)
108     {
109         keep[++tot]=b[edge[i].to];
110         put_road(edge[i].to);
111         tot--;
112     }
113 }
解法一

解法二:

我们可以存储逆边,逆向做一次$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;$$

由于字典序高位越小越好,满足最优子结构,贪心是可行的。

 1 #include<set>
 2 #include<map>
 3 #include<cmath>
 4 #include<ctime>
 5 #include<queue>
 6 #include<stack>
 7 #include<vector>
 8 #include<cstdio>
 9 #include<string>
10 #include<cstdlib>
11 #include<cstring>
12 #include<iostream>
13 #include<algorithm>
14 #define LL long long
15 #define RE register
16 #define IL inline
17 using namespace std;
18 const int N=2e6;
19 const int INF=~0u>>1;
20 
21 IL int Read()
22 {
23     int sum=0;
24     char c=getchar();
25     while (c<'0'||c>'9') c=getchar();
26     while (c>='0'&&c<='9') sum=sum*10+c-'0',c=getchar();
27     return sum;
28 }
29 IL void put(int d)
30 {
31     if (!d) return;
32     put(d/10);
33     putchar(d%10+'0');
34 }
35 
36 IL int Min(const int &a,const int &b) {return a<b ? a:b;}
37 IL int Max(const int &a,const int &b) {return a>b ? a:b;}
38 
39 int t,n,k,f,maxn;
40 int a[N+5];
41 int s[N+5];
42 
43 int main()
44 {
45     t=Read();
46     while (t--)
47     {
48         n=Read();k=Read();f=Read();
49         maxn=0;
50         memset(s,0,sizeof(s));
51         for (RE int i=1;i<=n;i++) a[i]=Read(),maxn=Max(maxn,a[i]),s[a[i]]++;
52         for (RE int i=1;i<=maxn;i++) s[i]+=s[i-1];
53         for(RE int d=1;d<=maxn;d++)
54             {
55                 int cnt=s[d-1];
56                 for(RE int j=d;cnt<=f&&j+k+1<=maxn;j+=d)
57                     if (j+k<Min(maxn,j+d-1)) cnt+=s[Min(maxn,j+d-1)]-s[j+k];
58                 if (cnt<=f) put(d),putchar(' ');
59             }
60         putchar('
');
61     }
62     return 0;
63 }
解法二
原文地址:https://www.cnblogs.com/NaVi-Awson/p/7405305.html