您的位置:首页 > 其它

HDU 3631 Shortest Path (Floyd的深层理解)

2015-03-08 22:31 417 查看
题目链接:点击打开链接

题目大意:有一张有向图,300个点,然后有10^5次操作,操作有两种,一种是激活某一个点,另一种是询问两个点之间的最短路径,这条路径必须由被激活的点构成。

显然任意两点之间的距离理想的是floyd。

这题让我对floyd有了更深的理解,以前一直觉得floyd非常好写,但是对于其原理一知半解,在这个变形应用里就遇到了困难。

我最开始非常原始的想法就是,如果floyd里加入一个点,多出来的运算如何表述。写了一发,只是找规律,没有理解其精髓。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
#define inf 0x3f3f3f3f
int dis[305][305];
bool vis[305];
int mark[305],nn,n,m,q;

int main(){
int u,v,len,op,cas = 1;
while(~scanf("%d%d%d",&n,&m,&q)){
if(n==0&&m==0&&q==0) break;
if(cas > 1) printf("\n");
printf("Case %d:\n",cas++);
nn = 0;

memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
for(int i = 0;i < n; i++) dis[i][i] = 0;
for(int i = 0;i < m; i++){
scanf("%d%d%d",&u,&v,&len);
dis[u][v] = min(len,dis[u][v]);
}
for(int i = 0;i < q; i++){
scanf("%d",&op);
if(op){
scanf("%d%d",&u,&v);
if(!vis[u]||!vis[v]){
printf("ERROR! At path %d to %d\n",u,v);
}
else{
if(dis[u][v] == inf) printf("No such path\n");
else{
printf("%d\n",dis[u][v]);
}
}
}
else{
scanf("%d",&u);
if(vis[u]){
printf("ERROR! At point %d\n",u);
}
else{
mark[nn] = u;//标记激活的点
vis[u] = 1;
//下面就是模拟每添加一个点多出来的计算
for(int i = 0;i < nn; i++)
for(int j = 0;j < nn; j++) dis[mark[j]][u] = min(dis[mark[j]][u],dis[mark[j]][mark[i]] + dis[mark[i]][u]);

for(int i = 0;i < nn; i++)
for(int j = 0;j <= nn; j++) dis[u][mark[j]] = min(dis[u][mark[j]],dis[u][mark[i]] + dis[mark[i]][mark[j]]);

for(int i = 0;i <= nn; i++)
for(int j = 0;j <= nn; j++) dis[mark[i]][mark[j]] = min(dis[mark[i]][mark[j]],dis[mark[i]][u] + dis[u][mark[j]]);
nn++;
}
}
}
}
return 0;
}
看了别人的题解以后,题解里提到严蔚敏老师的数据结构那本书,书的190页对floyd作了比较详细的原理解释。大致意思就是说,最外层循环枚举每一个可能通过的点,当枚举到k这个点的时候,dis[i][j]实际上就是从i到j中间经过序号不大于k的最短路径。而在内层两个循环中已经完成了i~k和k~j的最短路寻找。所以经过比较得到的就是最短路径。

总的来说,这道题的题意就是对floyd的原理的解读。

依照原理又写了一发。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;
#define inf 0x3f3f3f3f
int dis[305][305];
bool vis[305];
int n,m,q;

void floyd(int x){
for(int i = 0;i < n; i++) for(int j = 0; j < n; j++) dis[i][j] = min(dis[i][j],dis[i][x] + dis[x][j]);
}

int main(){
int u,v,len,op,cas = 1;
while(~scanf("%d%d%d",&n,&m,&q)){
if(n==0&&m==0&&q==0) break;
if(cas > 1) printf("\n");
printf("Case %d:\n",cas++);
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
for(int i = 0;i < n; i++) dis[i][i] = 0;
for(int i = 0;i < m; i++){
scanf("%d%d%d",&u,&v,&len);
dis[u][v] = min(len,dis[u][v]);
}
for(int i = 0;i < q; i++){
scanf("%d",&op);
if(op){
scanf("%d%d",&u,&v);
if(!vis[u]||!vis[v]){
printf("ERROR! At path %d to %d\n",u,v);
}
else{
if(dis[u][v] == inf) printf("No such path\n");
else{
printf("%d\n",dis[u][v]);
}
}
}
else{
scanf("%d",&u);
if(vis[u]){
printf("ERROR! At point %d\n",u);
}
else{
vis[u] = 1;
floyd(u);
}
}
}
}
return 0;
}


其实这应该是第四次学习floyd了,集训的时候学过一次,离散学过warshell,数据结构书里也有floyd,反而是这一次才真正领悟...

感悟:1.要多看书..2.学习不能功利...一知半解要不得

ps.这题交G++跑了C++两倍的时间,是什么神奇的原因呢...?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: