题目链接:https://ac.nowcoder.com/acm/contest/699/E
题目意思:有n个飞机场,给你起点s,终点e,有m种带权的无向边飞机线路。有k个线路,可以从中选一个免费飞行。问是否使用免费飞机票,并输出最短路花费。
思路:
1.先建普通飞机线路,算出最短路。再每一条免费线路重新算一次最短路,再判断是否比之前小。
spfa写法:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; struct node{ int u,v,w,next; }e[2050]; int head[1050],vis[1050],d[1050]; int st,ed,n,m,k,cnt; int from[1050],to[1050],val[1050]; void add(int u,int v,int w) { e[cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt++; } void spfa()//spfa算最短路 { memset(d,0x3f,sizeof(d)); memset(vis,0,sizeof(vis)); queue<int> q; vis[st]=1; d[st]=0; q.push(st); while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i!=-1;i=e[i].next) { int v=e[i].v; if(d[v]>d[u]+e[i].w) { d[v]=d[u]+e[i].w; if(!vis[v]) { vis[v]=1; q.push(v); } } } } } int main() { int u,v,w; cnt=0; memset(head,-1,sizeof(head)); std::ios::sync_with_stdio(false); cin>>n>>st>>ed>>m; for(int i=1;i<=m;i++) { cin>>from[i]>>to[i]>>val[i]; add(from[i],to[i],val[i]); add(to[i],from[i],val[i]); } spfa(); int ans=d[ed];//记录没有用免费机票的最短路 int anss=inf; cin>>k; for(int i=0;i<k;i++) { cin>>u>>v; add(u,v,0); add(v,u,0); spfa();//每条免费线路算一次最短路 anss=min(anss,d[ed]); cnt=0; memset(head,-1,sizeof(head));//记得初始化 for(int i=1;i<=m;i++) { add(from[i],to[i],val[i]); add(to[i],from[i],val[i]); } } if(ans==anss) cout<<"No"<<endl<<ans<<endl; else cout<<"Yes"<<endl<<anss<<endl; return 0; }
dijkstra写法:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define inf 0x3f3f3f3f #include<queue> using namespace std; typedef long long ll; struct node{ int u,v,w,next; }e[2050]; struct nd{ int id; int dis; bool operator <(const nd &a)const{ return a.dis<dis; } }; int head[1050],vis[1050],d[1050]; int st,ed,n,m,k,cnt; int from[1050],to[1050],val[1050]; void add(int u,int v,int w) { e[cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt++; } void dij()//优先队列优化 { priority_queue<nd> q; memset(d,0x3f,sizeof(d)); memset(vis,0,sizeof(vis)); d[st]=0; while(!q.empty()) q.pop(); q.push({st,0}); while(!q.empty()) { nd t=q.top(); q.pop(); int u=t.id; if(vis[u]) continue; vis[u]=1; for(int i=head[u];i!=-1;i=e[i].next) { int v=e[i].v; if(!vis[v]&&d[v]>d[u]+e[i].w) { d[v]=d[u]+e[i].w; q.push({v,d[v]}); } } } } int main() { int u,v,w; cnt=0; memset(head,-1,sizeof(head)); std::ios::sync_with_stdio(false); cin>>n>>st>>ed>>m; for(int i=1;i<=m;i++) { cin>>from[i]>>to[i]>>val[i]; add(from[i],to[i],val[i]); add(to[i],from[i],val[i]); } dij(); int ans=d[ed]; int anss=inf; cin>>k; for(int i=0;i<k;i++) { cin>>u>>v; add(u,v,0); add(v,u,0); dij(); anss=min(anss,d[ed]); cnt=0; memset(head,-1,sizeof(head)); for(int i=1;i<=m;i++) { add(from[i],to[i],val[i]); add(to[i],from[i],val[i]); } } if(ans==anss) cout<<"No"<<endl<<ans<<endl; else cout<<"Yes"<<endl<<anss<<endl; return 0; }
2.开始就算出起点到所有点的最短距离和终点到所有点的最短路,在每次免费线路直接判断是否小就可以。因为是无向边,可以直接算终点到所有点的距离,如果是有向边,只要反向建边再算即可。这个思路只算了两次最短路,明显是上面快很多的。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define inf 0x3f3f3f3f #include<queue> using namespace std; typedef long long ll; struct node{ int u,v,w,next; }e[2050]; struct nd{ int id; int dis; bool operator <(const nd &a)const{ return a.dis<dis; } }; int head[1050],vis[1050],d[1050][1050]; int st,ed,n,m,k,cnt; void add(int u,int v,int w) { e[cnt].u=u; e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; head[u]=cnt++; } void dij(int start) { priority_queue<nd> q; memset(vis,0,sizeof(vis)); d[start][start]=0; while(!q.empty()) q.pop(); q.push({start,0}); while(!q.empty()) { nd t=q.top(); q.pop(); int u=t.id; if(vis[u]) continue; vis[u]=1; for(int i=head[u];i!=-1;i=e[i].next) { int v=e[i].v; if(!vis[v]&&d[start][v]>d[start][u]+e[i].w) { d[start][v]=d[start][u]+e[i].w; q.push({v,d[start][v]}); } } } } int main() { int u,v,w,flag=0; cnt=0; memset(head,-1,sizeof(head)); std::ios::sync_with_stdio(false); cin>>n>>st>>ed>>m; for(int i=1;i<=m;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } memset(d,0x3f,sizeof(d)); dij(st);//算出起点到所有点的最短路。 int ans=d[st][ed]; dij(ed);//算出终点到所有点的最短路 int anss=inf; // for(int i=1;i<=n;i++) // cout<<i<<' '<<d[st][i]<<' '<<d[ed][i]<<endl; cin>>k; for(int i=0;i<k;i++) { cin>>u>>v; if(ans>d[st][u]+d[ed][v]||ans>d[st][v]+d[ed][u])//判断是否用免费机票 { anss=min(anss,min(d[st][u]+d[ed][v],d[st][v]+d[ed][u])); flag=1; } } if(!flag) cout<<"No"<<endl<<ans<<endl; else cout<<"Yes"<<endl<<anss<<endl; return 0; }