与普通的最短路径不同的是,题目中新引入了一个计入总体的费用——城市建设费。由于城市建设费由整体的某最大值决定,导致解没有最优子结构的性质,给思维带来难度。
既然最棘手的是城市建设费,我们就对它分类讨论。为了分类有效,我们先把城市繁华度从小到大排个序,这样分类讨论时当前路径的城市的最大繁华度即为不严格递增的。这样,我们便可以考虑当前路径只可能经过前k个最不繁华的城市(可以不经过,但只要经过,经过的城市就一定在那前k个最不繁华的城市中),让k从小到大递增(每次+1)至n,对于每个k值,更新一下最短路径,再看一下第k个最不繁华的城市是否可以更新答案。由此我们想到了FLOYD算法。
见AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cctype> 5 #include<cstring> 6 7 using namespace std; 8 9 int ans,n,m,q,a[251][251],f[251][251],p[251],t[251]; 10 11 char ch; 12 13 int read() 14 { 15 ans=0; 16 ch=getchar(); 17 while(!isdigit(ch)) ch=getchar(); 18 while(isdigit(ch)) ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar(); 19 return ans; 20 } 21 22 int cmp(int x,int y)//排序最终效果:把t数组排序,使t数组满足编号为ti的城市是第i个最不繁华的城市 23 { 24 return p[x]<p[y]; 25 } 26 27 int main() 28 { 29 // freopen("road.in","r",stdin); 30 // freopen("road.out","w",stdout); 31 memset(a,63,sizeof a);//把a数组(存两点间最短路径)初始化为一个足以解题的无穷大 32 n=read(),m=read(),q=read(); 33 for(int i=1;i<=n;i++) p[i]=read(); 34 for(int i=1;i<=n;i++) t[i]=i;//一开始t数组应有所有城市的编号,所以不能忘了初始化 35 int u,v,w; 36 for(int i=1;i<=m;i++) 37 { 38 u=read(),v=read(),w=read(); 39 a[u][v]=min(a[u][v],w);//用邻接矩阵存边不要忘了防重边 40 a[v][u]=min(a[v][u],w); 41 } 42 for(int i=1;i<=n;i++) a[i][i]=0; 43 for(int i=1;i<=n;i++) //初始化两城市直接连通(路径不经过其他任何城市)的情况 44 for(int j=1;j<=n;j++) 45 f[i][j]=a[i][j]+max(p[i],p[j]); 46 sort(t+1,t+1+n,cmp); 47 for(int k=1;k<=n;k++)//当FLOYD的最外层循环枚举到k是,路径经过的中间城市只可能是前k个最不繁华的城市(因为目前只用他们进行了松弛操作) 48 for(int i=1;i<=n;i++) 49 for(int j=1;j<=n;j++) 50 { 51 a[i][j]=min(a[i][j],a[i][t[k]]+a[t[k]][j]); 52 f[i][j]=min(f[i][j],a[i][j]+max(p[i],max(p[j],p[t[k]]))); 53 } 54 for(int i=1;i<=q;i++) 55 { 56 u=read();v=read(); 57 printf("%d ",f[u][v]); 58 } 59 return 0; 60 }
总结:算法中有很多细节。初始时的边界条件要记得考虑。如果一开始对某个变量的有要求,就不能忘了初始化。用邻接矩阵存边的话不要忘了去重边。