#3087. 「GXOI / GZOI2019」旅行者

原题链接

考察:最短路+思维

完全不会,废物本废

错误思路:

       比较明显的思路就是以每个特殊点为起点,然后Dijkstra,再以每个特殊点为终点求最小值,显而易见地TLE

解法一(官方题解):

       由暴力思路延伸来的思路.因为我们只需要知道最小值,而不是特殊点.所以起点和终点是谁无所谓.所以可以将上述的一个个求最小值优化为一组组求最小值.

       比较难想的如何分组(对本蒟蒻而言),这题的分组思路是枚举二进制的第i位,如果第i位是1,就作为起点,如果第i位是0,就作为终点.但是这道题求完Dijkstra后还需要一个个枚举终点距离,更好的做法是建立虚点S,E.S与起点建立0的边,E与终点建立0的边.

       n最多有17位,所以进行17次dijkstra.但是注意是需要起点终点互换求两次,否则无法覆盖全部方案.

      时间复杂度:O(T*17*2*m*log2n)

 1 #include <iostream>
 2 #include <cstring>
 3 #include <queue> 
 4 #include <vector>
 5 using namespace std;
 6 typedef long long LL;
 7 typedef pair<LL,int> PII;
 8 const int N = 100110,M = 500010;
 9 int n,m,k,idx,h[N],ask[N],backup[N],S,E;
10 LL dist[N],res = (1ll<<63)-1;
11 bool st[N];
12 struct Road{
13     int to,ne,w;
14 }road[M+N];
15 void dijkstra(int s,int e)
16 {
17     for(int i=1;i<=n+3;i++) dist[i] = 1e14;
18     memset(st,0,sizeof st);
19     dist[s] = 0;
20     priority_queue<PII,vector<PII>,greater<PII> > q;
21     q.push({0,s});
22     while(q.size())
23     {
24         PII it = q.top();
25         int u = it.second;
26         q.pop();
27         if(st[u]) continue;
28         st[u] = 1;
29         for(int i=h[u];~i;i=road[i].ne)
30         {
31             int v = road[i].to;
32             if(dist[v]>dist[u]+road[i].w)
33             {
34                 dist[v] = dist[u]+road[i].w;
35                 q.push({dist[v],v});
36             }
37         }
38     }
39     res = min(res,dist[e]);
40 }
41 void add(int a,int b,int w)
42 {
43     road[idx].to = b,road[idx].w = w,road[idx].ne = h[a],h[a] = idx++;
44 }
45 int main()
46 {
47     int T;
48     scanf("%d",&T);
49     while(T--)
50     {
51         memset(h,-1,sizeof h); idx = 0;
52         res = (1ll<<63)-1;
53         scanf("%d%d%d",&n,&m,&k);
54         while(m--)
55         {
56             int a,b,c; scanf("%d%d%d",&a,&b,&c);
57             if(a==b) continue;
58             add(a,b,c);
59         }
60         for(int i=1;i<=k;i++) scanf("%d",&ask[i]);
61         S = n+1,E = n+2;
62         int t = idx;
63         for(int i=1;i<=n+3;i++) backup[i] = h[i];
64         for(int i=0;i<=17;i++)
65         {
66             idx = t,memcpy(h,backup,sizeof h);
67             for(int j=1;j<=k;j++)
68             {//1作起点 
69                    if(ask[j]>>i&1) add(S,ask[j],0);
70                   else add(ask[j],E,0);//0作终点 
71             }
72                dijkstra(S,E);
73                int t = idx;
74             for(int i=1;i<=n+3;i++) h[i] = backup[i];
75            for(int j=1;j<=k;j++)
76             {//1作终点 
77                    if((ask[j]>>i&1)) add(ask[j],E,0);
78                   else add(S,ask[j],0);//0作终点 
79             }
80            dijkstra(S,E);
81         }
82         printf("%lld\n",res);
83     }
84     return 0;
85 }
解法一

解法二(神仙思路):

      易知答案一定经过了某条边.所以枚举边{u,v,w} 先求所有特殊点到u的最短距离,再求v到所有特殊点的最短距离. ans = dist[u]+此路权值+dist[v]

      因为涉及v到所有特殊点的最短距离,所以需要建立反向边.但这里有个特殊情况.就是如果u,v最近的特殊点是同一个答案就不合法.所有需要在Dijkstra时求出距离每个点最近的特殊点.

     

 1 #include <iostream>
 2 #include <cstring>
 3 #include <queue> 
 4 #include <vector>
 5 using namespace std;
 6 typedef long long LL;
 7 typedef pair<LL,int> PII;
 8 const int N = 100110,M = 500010;
 9 int n,m,k,idx,h[N],ask[N],a[M],b[M],w[M];
10 int color[2][N];
11 bool st[N];
12 LL dist[2][N];
13 struct Road{
14     int to,ne,w;
15 }road[M+N]; 
16 void add(int a,int b,int w)
17 {
18     road[idx].to = b,road[idx].w = w,road[idx].ne = h[a],h[a] = idx++;
19 }
20 void dijkstra(int p)
21 {
22     priority_queue<PII,vector<PII>,greater<PII> > q;
23     for(int i=1;i<=n;i++) dist[p][i] = 1e14,st[i] = 0,color[p][i] = 0;
24     for(int i=1;i<=k;i++) dist[p][ask[i]] = 0,color[p][ask[i]] = ask[i],q.push({0,ask[i]});
25     while(q.size())
26     {
27         PII it = q.top();
28         q.pop();
29         int u = it.second;
30         if(st[u]) continue;
31         st[u] = 1;
32         for(int i=h[u];~i;i=road[i].ne)
33         {
34             int v = road[i].to;
35             if(dist[p][v]>dist[p][u]+road[i].w)
36             {
37                 dist[p][v] = dist[p][u]+road[i].w;
38                 color[p][v] = color[p][u];//标记离v点最近的u 
39                 q.push({dist[p][v],v}) ;
40             }
41         }
42     }
43 }
44 int main()
45 {
46     int T;
47     scanf("%d",&T);
48     while(T--)
49     {
50         memset(h,-1,sizeof h); idx = 0;
51         scanf("%d%d%d",&n,&m,&k);
52         for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i],&b[i],&w[i]);
53         for(int i=1;i<=k;i++) scanf("%d",&ask[i]);
54         //正向建边
55         for(int i=1;i<=m;i++) 
56             if(a[i]!=b[i]) add(a[i],b[i],w[i]);
57         dijkstra(0);//作起点的时候
58         //反向建边
59         memset(h,-1,sizeof h); idx = 0;
60         for(int i=1;i<=m;i++)
61             if(a[i]!=b[i]) add(b[i],a[i],w[i]);
62         dijkstra(1);
63         LL ans = (1ll<<63)-1;
64         for(int i=1;i<=m;i++)
65           if(color[0][a[i]]&&color[1][b[i]]&&color[0][a[i]]!=color[1][b[i]]) 
66             ans = min(dist[0][a[i]]+dist[1][b[i]]+w[i],ans);
67         printf("%lld\n",ans);
68     }
69     return 0;
70 }

 

posted @ 2021-05-01 22:41  acmloser  阅读(70)  评论(0编辑  收藏  举报