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\n"); 118 else 119 printf("%lld\n",ans[i]); 120 } 121 printf("\n"); 122 123 return ; 124 }