P1186 玛丽卡
题目描述
麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复。
因为她和他们不住在同一个城市,因此她开始准备她的长途旅行。
在这个国家中每两个城市之间最多只有一条路相通,并且我们知道从一个城市到另一个城市路上所需花费的时间。
麦克在车中无意中听到有一条路正在维修,并且那儿正堵车,但没听清楚到底是哪一条路。无论哪一条路正在维修,从玛丽卡所在的城市都能到达麦克所在的城市。
玛丽卡将只从不堵车的路上通过,并且她将按最短路线行车。麦克希望知道在最糟糕的情况下玛丽卡到达他所在的城市需要多长时间,这样他就能保证他的女朋友离开该城市足够远。
编写程序,帮助麦克找出玛丽卡按最短路线通过不堵车道路到达他所在城市所需的最长时间(用分钟表示)。
输入输出格式
输入格式:
第一行有两个用空格隔开的数NN和MM,分别表示城市的数量以及城市间道路的数量。1≤N≤1000,1≤M≤N (N-1)/2,1≤N≤1000,1≤M≤N×(N−1)/2。城市用数字1-N1−N标识,麦克在城市1中,玛丽卡在城市N中。
接下来的MM行中每行包含三个用空格隔开的数A,B,V,其中1≤A,B≤N,1≤V≤10001≤A,B≤N,1≤V≤1000。这些数字表示在A和城市B中间有一条双行道,并且在V分钟内就能通过。
输出格式:
一行,写出用分钟表示的最长时间,在这段时间中,无论哪条路在堵车,玛丽卡应该能够到达麦克处,如果少于这个时间的话,则必定存在一条路,该条路一旦堵车,玛丽卡就不能够赶到麦克处。
输入输出样例
输出样例#1:
View Code
View Code
27
解析:
这个题的题目背景真是引人深思,,,我们在集体中要控制好自己的情绪,
在学校中我们要控制好自己,不能与异性过度交往。。。。
好了,废话少说,下面进入正题:
首先我们把这道题的意思理解一下:删除一条边后,最短路的最大值.
自然而然地想到依次屏蔽每一条边,之后求最短路,于是就有了下面这份代码:
1 #include<iostream> 2 #include<cstring> 3 #include<queue> 4 #define inf 336860180 5 using namespace std; 6 int n,m,k,e,v[2001001],head[2001001],nxt[2001001],w[2001001],dist[210001],total,b1,b2,b3,d,dp[101],No; 7 bool pd[100001]; 8 void add(int a,int b,int c) 9 { 10 total++; 11 v[total]=b; 12 w[total]=c; 13 nxt[total]=head[a]; 14 head[a]=total; 15 return; 16 } 17 void spfa() 18 { 19 memset(dist,20,sizeof(dist)); 20 memset(pd,0,sizeof(pd)); 21 queue<int>q; 22 q.push(1); 23 dist[1]=0; 24 pd[1]=1; 25 while(q.size()) 26 { 27 int x=q.front(); 28 q.pop(); 29 pd[x]=0; 30 for(int i=head[x];i!=-1;i=nxt[i]) 31 { 32 int y=v[i]; 33 if(dist[y]>dist[x]+w[i]) 34 { 35 dist[y]=dist[x]+w[i]; 36 if(!pd[y]) 37 { 38 q.push(y); 39 pd[y]=1; 40 } 41 } 42 } 43 } 44 } 45 int main() 46 { 47 memset(head,-1,sizeof(head)); 48 cin>>n>>m; 49 for(int i=1;i<=m;i++) 50 { 51 cin>>b1>>b2>>b3; 52 add(b1,b2,b3); 53 add(b2,b1,b3); 54 } 55 spfa(); 56 int ans=dist[n]; 57 for(int i=1;i<=total;i++) 58 { 59 int tmp=w[i]; 60 w[i]=inf+100; 61 spfa(); 62 ans=max(ans,dist[n]); 63 w[i]=tmp; 64 } 65 cout<<ans; 66 return 0; 67 }
高高兴兴地把这份代码交到测评机上,就会TLE。
会T4个点。
造成这种现象的原因并不是选择了SPFA(SPFA不背锅),而是多次求最短路的结果,
显然这张图是稠密图,最多可以到达数十万条边,而求那么多次最短路,结果只有TLE。
于是我们开始谋求一些改进的方法;
对于这些边,显然我们有许多条是重复计算的,也就是说,屏蔽这些边根本对最短路的结果没有任何影响
我们要屏蔽的,显然是那些不满足三角形不等式的,可以让最短路进行松弛操作的边。
如何把这些边记录下来并进行“屏蔽”操作呢???
我选取的策略是,首先求一次没有屏蔽任何边的最短路,记录下可以进行松弛操作的边,换言之,
就是把可能对最短路有影响的便都记录下来;
同时还要把起点记录下来,开两个数组就行了。
由于我不想搞两个SPFA的函数,更不想打两遍SPFA(即使是复制也不想),所以我搞了个s参数,当这个参数
为1时,表示第一次求最短路,需要在此时记录可以松弛的边和节点,当参数s为0时,表示不是第一次求最短路。就无需进行这个操作了;
下面给出AC代码:
1 #include<iostream> 2 #include<cstring> 3 #include<queue> 4 #define inf 336860180 5 using namespace std; 6 int n,m,k,e,v[2001001],head[2001001],nxt[2001001],w[2001001],dist[210001],total,b1,b2,b3,nxxt[100011],point[100101]; 7 bool pd[100001]; 8 void add(int a,int b,int c)//邻接表 9 { 10 total++; 11 v[total]=b; 12 w[total]=c; 13 nxt[total]=head[a]; 14 head[a]=total; 15 return; 16 } 17 void spfa(int cc,int s)//最短路,s记录是否第一次求最短路 18 { 19 memset(dist,20,sizeof(dist)); 20 memset(pd,0,sizeof(pd)); 21 queue<int>q; 22 q.push(1); 23 dist[1]=0; 24 pd[1]=1; 25 while(q.size()) 26 { 27 int x=q.front(); 28 q.pop(); 29 pd[x]=0; 30 for(int i=head[x];i!=-1;i=nxt[i]) 31 { 32 if(i==cc)continue; 33 int y=v[i]; 34 if(dist[y]>dist[x]+w[i]) 35 { 36 dist[y]=dist[x]+w[i]; //松弛 37 if(s) 38 { 39 point[y]=i;//记起点 40 nxxt[y]=x;//记边 41 } 42 if(!pd[y]) 43 { 44 q.push(y); 45 pd[y]=1; 46 } 47 } 48 } 49 } 50 } 51 int main() 52 { 53 memset(head,-1,sizeof(head)); 54 cin>>n>>m; 55 for(int i=1;i<=m;i++) 56 { 57 cin>>b1>>b2>>b3; 58 add(b1,b2,b3); 59 add(b2,b1,b3); 60 } 61 spfa(0,1); 62 int ans=dist[n]; 63 for(int i=n;i;i=nxxt[i]) 64 { 65 spfa(point[i],0);//屏蔽 66 ans=max(ans,dist[n]); 67 } 68 cout<<ans; 69 return 0; 70 }
最后,祝大家NOIP2019 RP++!!!
这么伟大的题解,不关注+素质三连吗???