http://acm.hdu.edu.cn/showproblem.php?pid=1595
题目大意:
从城市1到城市n有m条双向路,其中有一条路是坏的,但是我们不知道到底哪条路是坏的。
考虑到最坏情况,我们从城市1走到城市n所需要的最少时间是多少。
解题思路:
起初,想法很单纯,枚举每一条边为坏边,求1到n的最短路,然后在这些最短路中取最大值。
思路是正确的,但是会超时,路径的条数达到100w,每次求最短路的复杂度为O(n),所以总的复杂度是O(n*100w)。
这个复杂度是无法接受的。
后来看了某位牛人的解法,深受启发。
首先假设所有的路径都是好的,求取1到n的最短路,并且记录下最短路路径上的边。
接着,枚举最短路径上的边为坏边,求取1到n的最短路,在最短路中取最大值。
这样,我们就不需要枚举所有的边,而只是枚举最短路上的边,可以大大降低复杂度。
源代码:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=1005;
const int maxm=1000005;
const int INF=99999999;
int first[maxn];
int next[maxm],to[maxm],v[maxm],ecnt;
int dis[maxn],que[maxn*maxn],vis[maxn],pre[maxn],key[maxn];
int n,m,ans;
vector<int> vec;
inline void add(int a,int b,int c)
{
to[ecnt]=b;
v[ecnt]=c;
next[ecnt]=first[a];
first[a]=ecnt++;
to[ecnt]=a;
v[ecnt]=c;
next[ecnt]=first[b];
first[b]=ecnt++;
return;
}
inline void init()
{
for(int i=1;i<=n;i++)
{
dis[i]=INF;
vis[i]=0;
}
}
int spfa()
{
init();
dis[1]=0;
vis[1]=1;
int f=0,r=1;
que[f]=1;
while(f<r)
{
int now=que[f++];
vis[now]=0;
for(int i=first[now];i!=-1;i=next[i])
{
if(v[i]==0) continue;
int kid=to[i];
if(dis[now]+v[i]<dis[kid])
{
pre[kid]=now; //记录下父亲节点
key[kid]=i; //记录下边
dis[kid]=dis[now]+v[i];
if(!vis[kid])
{
vis[kid]=1;
que[r++]=kid;
}
}
}
}
return dis[n];
}
void solve()
{
ans=0;
spfa(); //先跑最短路求取最短路径
vec.clear();
int now=n;
while(now!=1)
{
vec.push_back(key[now]);
now=pre[now];
}
int len=vec.size();
for(int i=0;i<len;i++) //枚举最短路径上的边
{
int b=vec[i];
int t=v[b];
v[b]=v[b^1]=0;
ans=max(spfa(),ans);
v[b]=v[b^1]=t;
}
printf("%d\n",ans);
return;
}
int main()
{
// freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&m)==2)
{
ecnt=0;
memset(first,-1,sizeof(first));
while(m--)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
solve();
}
return 0;
}