J题: xay loves Floyd
原题链接:https://ac.nowcoder.com/acm/contest/11258/J
原题大意
有人将Floyd算法写错成以下形式:
for i from 1 to n
for j from 1 to n
for k from 1 to n
dis[i][j] <- min(dis[i][j],dis[i][k] + dis[k][j])
给定
n
(
1
≤
n
≤
2000
)
n(1\le n\le 2000)
n(1≤n≤2000) 个点
m
(
1
≤
m
≤
5000
)
m(1\le m\le 5000)
m(1≤m≤5000) 条边的有向图,求按错误的Floyd算法执行后,有多少
d
i
s
[
i
]
[
j
]
dis[i][j]
dis[i][j] 和正确的算法结果一样。 对于
i
i
i 无法到达
j
j
j 的情况,
d
i
s
[
i
]
[
j
]
=
∞
dis[i][j]=\infin
dis[i][j]=∞ ,在判断错误结果与正确结果是否一样时认为
∞
=
∞
\infin =\infin
∞=∞ 。
题解
观察算法易发现,最终求得的dis数组的值应不小于正解dis数组的值。 可以采用暴力+优化剪枝的做法,首先考虑正解的求得。 正解只需考虑正确性,Dijstra算法计算单源最短路的复杂度为
O
(
m
l
o
g
n
)
O(m_{log}n)
O(mlog?n) ,若遍历每个点计算则总复杂度为
O
(
n
m
l
o
g
n
)
O(nm_{log}n)
O(nmlog?n) ,可用其替代Floyd。 错误Floyd的复杂度为
O
(
n
3
)
O(n^3)
O(n3) ,且不能用其他算法代替,但是我们可以剪枝以减小复杂度,我们每次对边权取
min
?
\min
min ,而且最终的错误数组只可能更大不可能更小,所以我们当一下情况时可以跳过
k
k
k 循环的计算:
- 最优解为
∞
\infin
∞ ;
- 当前dis值已经与正解相同
而且我们可以只遍历dis值正确的边,因为若dis[i][j]的值不是最优的,则通过dis[i][j]计算得到的边也不会是最优,因此我们并不用在往后的循环中遍历该条边。
参考代码
#include<bits/stdc++.h>
#define ll long long
#define gc() getchar()
#define For(i,n,m) for(int i=n;i<=m;i++)
#define FOR(i,n,m) for(int i=n;i>=m;i--)
#define pb push_back
using namespace std;
void read(int &x){
int ret=0;
char c=gc(),last=' ';
while(!isdigit(c))last=c,c=gc();
while(isdigit(c))ret=ret*10+c-'0',c=gc();
x=last=='-'?-ret:ret;
}
struct node{
int u,w;
node(int u=0,int w=0):u(u),w(w){}
friend bool operator<(const node &a,const node &b){return a.w>b.w;}
};
const int MAXN=2e3+5,INF=0x3f3f3f3f;
int n,m,dis[MAXN][MAXN],vis[MAXN],d[MAXN],r[MAXN][MAXN];
vector<node>e[MAXN];
void dij(int x)
{
memset(vis,0,sizeof(vis));
memset(d,INF,sizeof(d));
priority_queue<node>q;
d[x]=0;
q.push(node(x,0));
int u,v,w;
while(!q.empty()){
u=q.top().u,q.pop();
vis[u]=1;
if(e[u].empty())continue;
For(i,0,e[u].size()-1){
v=e[u][i].u,w=e[u][i].w;
if(vis[v])continue;
if(d[v]>d[u]+w){
d[v]=d[u]+w;
q.push(node(v,d[v]));
}
}
}
For(i,1,n)r[x][i]=d[i];
}
int main()
{
read(n),read(m);
int u,v,w;
memset(dis,INF,sizeof(dis));
For(i,1,n)dis[i][i]=0;
For(i,1,m){
read(u),read(v),read(w);
dis[u][v]=w;
e[u].pb(node(v,w));
}
For(i,1,n)dij(i);
For(i,1,n){
if(e[i].empty())continue;
For(j,1,n){
if(i==j)continue;
if(dis[i][j]==r[i][j])continue;
if(r[i][j]==INF)continue;
int k;
For(l,0,e[i].size()-1){
k=e[i][l].u;
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
if(dis[i][j]==r[i][j])e[i].pb(node(j,dis[i][j]));
}
}
int ans=0;
For(i,1,n)For(j,1,n)if(dis[i][j]==r[i][j])ans++;
cout<<ans<<endl;
return 0;
}
|