POJ 4046 Sightseeing 枚举+最短路 好题

有n个节点的m条无向边的图,节点编号为1~n

然后有点权和边权,给出q个询问,每一个询问给出2点u,v

输出u,v的最短距离

这里的最短距离规定为:

u到v的路径的所有边权+u到v路径上最大的一个点权的和(点权也可以是u,v)

n<=1000

m<=20000

Q<=20000

时限:5000ms

没有点权的话,好处理

加了点权呢?

我们可以先枚举n个节点,跑n次spfa,当枚举节点u时,我们默认节点u是所有路径上点权最大的一个点

即我们枚举节点u时,我们先把点权比u大的节点删除了,在剩下的图跑spfa

但实际上只要在spfa时加一句判断即可,并不用真的去重建图

这样对于每一个询问,我们只要枚举点权最大的点就可以了,每一个询问是Q(n)

ans=min(dis[i][u]+dis[i][v]+cost[i])

但是这样我还是tle了, 因为这样需要开一个2维数组

dis[i][j]表示以第i个点为起点,到达节点j的短路

最后选择先存下所有询问,离线更新ans,在spfa的同时枚举更新

为什么这样就可以了呢?

因为一维数组比二维数组快很多。

从这道题:

1.当点权和边权混在一起比较难求的时候,我们可以先假设点权最大,分离出点权和边权,再枚举每一个点,就可以了

不止这个, 枚举的思想在很多时候都很好用

2.用stl的queue,时间是4000ms左右,用自己的队列,时间是3500左右,快了半秒

3.一维数组的访问比二维数组快很多

4.做题的时候要注意,循环的时候的终止条件,是到n? m? 还是Q?这个写错就wa了,又难发现

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 
  5 #define ll long long
  6 
  7 using namespace std;
  8 
  9 const int maxn=1005;
 10 const int maxm=20000+5;
 11 const ll inf=0x3f3f3f3f3f3f3f3f;
 12 
 13 struct Edge
 14 {
 15     int to,next;
 16     ll w;
 17 };
 18 Edge edge[maxm<<1];
 19 int head[maxn];
 20 int tot;
 21 int que[maxm<<2];
 22 ll ans[maxm];
 23 int a[maxm];
 24 int b[maxm];
 25 ll dis[maxn];
 26 ll cost[maxn];
 27 bool vis[maxn];
 28 int n,Q;
 29 
 30 void init()
 31 {
 32     memset(head,-1,sizeof head);
 33     tot=0;
 34 }
 35 
 36 void addedge(int u,int v,ll w)
 37 {
 38     edge[tot].to=v;
 39     edge[tot].w=w;
 40     edge[tot].next=head[u];
 41     head[u]=tot++;
 42 }
 43 
 44 void solve();
 45 
 46 int main()
 47 {
 48     int m;
 49     while(scanf("%d %d",&n,&m)){
 50         if(!n&&!m)
 51             break;
 52 
 53         for(int i=1;i<=n;i++)
 54             scanf("%lld",&cost[i]);
 55         int u,v;
 56         ll w;
 57         init();
 58         for(int i=1;i<=m;i++){
 59             scanf("%d %d %lld",&u,&v,&w);
 60             addedge(u,v,w);
 61             addedge(v,u,w);
 62         }
 63         scanf("%d",&Q);
 64         for(int i=1;i<=Q;i++){
 65             scanf("%d %d",&a[i],&b[i]);
 66         }
 67 
 68         solve();
 69     }
 70     return 0;
 71 }
 72 
 73 void spfa(int srt)
 74 {
 75     for(int i=1;i<=n;i++)
 76         dis[i]=inf;
 77     dis[srt]=0;
 78     memset(vis,false,sizeof vis);
 79     int l=0,r=0;
 80     que[r++]=srt;
 81     vis[srt]=true;
 82     while(l<r){
 83         int u=que[l++];
 84         vis[u]=false;
 85         for(int i=head[u];~i;i=edge[i].next){
 86             int v=edge[i].to;
 87             ll w=edge[i].w;
 88             if(cost[v]>cost[srt])
 89                 continue;
 90             if(dis[v]>dis[u]+w){
 91                 dis[v]=dis[u]+w;
 92                 if(!vis[v]){
 93                     que[r++]=v;
 94                     vis[v]=true;
 95                 }
 96             }
 97         }
 98     }
 99     for(int i=1;i<=Q;i++){
100         if(dis[a[i]]==inf||dis[b[i]]==inf)
101             continue;
102         if(dis[a[i]]+dis[b[i]]+cost[srt]<ans[i])
103             ans[i]=dis[a[i]]+dis[b[i]]+cost[srt];
104     }
105 }
106 
107 void solve()
108 {
109     for(int i=1;i<=Q;i++)
110         ans[i]=inf;
111     for(int i=1;i<=n;i++){
112         spfa(i);
113     }
114 
115     for(int i=1;i<=Q;i++){
116         if(ans[i]==inf)
117             printf("-1
");
118         else
119             printf("%lld
",ans[i]);
120     }
121     printf("
");
122 
123     return ;
124 }
原文地址:https://www.cnblogs.com/-maybe/p/4814954.html