以杭电2544题目为例
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
3 2
//bellman_ford算法,求单源到其它节点的最短路,可以处理含有负权的边,并且能判断图中是否存在负权回路(这一条在一些题中也有应用) //无向图转化为有向图,边数加倍,构造边结构体,没用到邻接矩阵 #include <iostream> using namespace std; const int maxNodeNum=110;//最多节点个数 const int maxEdgeNum=10010;//最多边条数 int nodeNum,edgeNum;//节点,有向边个数 int dis[maxNodeNum];//从单源点到各个点的距离 const int inf=0x3f3f3f3f;//边的权重无穷大数 bool loop;//判断是否存在负权回路 struct Edge { int s,e,w; }edge[maxEdgeNum*2];//构造边,这里因为是无向图,要看成有向处理。 void bellman_ford(int start) { //第一步:赋初值 for(int i=1;i<=nodeNum;i++) dis[i]=inf; dis[start]=0; //第二步,对边进行松弛更新操作 for(int i=1;i<=nodeNum-1;i++)//最短路径为简单路径不可能含有环,图中最多有nodeNum-1条边 { bool ok=0; for(int j=1;j<=edgeNum;j++) { if(dis[edge[j].s]+edge[j].w<dis[edge[j].e])//松弛 { dis[edge[j].e]=dis[edge[j].s]+edge[j].w; ok=1; } } if(ok==0) break; } //第三步,判断图中是否存在负权环 loop=0; for(int i=1;i<=edgeNum;i++) if(dis[edge[i].s]+edge[i].w<dis[edge[i].e]) loop=1; } int main()//125MS { while(cin>>nodeNum>>edgeNum&&(nodeNum||edgeNum)) { int from,to,w; int cnt=1; for(int i=1;i<=edgeNum;i++)//无向图,一条无向边看为两条有向边 { cin>>from>>to>>w; edge[cnt].s=edge[cnt+1].e=from; edge[cnt].e=edge[cnt+1].s=to; edge[cnt++].w=w; edge[cnt++].w=w;//切记,不能写成 edge[cnt++]=edge[cnt++].w; } edgeNum*=2;//无向图 bellman_ford(1); cout<<dis[nodeNum]<<endl; } return 0; }
//SPFA算法,是对bellman_ford算法的优化,采用队列,在队列中取点对其相邻的点进行松弛操作 //如果松弛成功且相邻的点不在队列中,就把相邻的点加入队列,被取的点出队,并把它的状态(是否在队列中) //改为否,vis[i]=0,同一个节点可以多次进入队列进行松弛操作 //这样的题操作步骤:首先建立邻接矩阵,邻接矩阵初始化为inf,注意需要判断一下输入的边是否小于已有的边,取最小的那个,因为可能有重边, //建立完邻接矩阵,写SPFA函数,dis[]数组初始化为inf,源点dis[start]=0 #include <iostream> #include <string.h> #include <queue> using namespace std; const int maxNodeNum=110;//最多节点个数 const int maxEdgeNum=10010;//最多边条数 const int inf=0x3f3f3f3f;//边的权重无穷大数 int nodeNum,edgeNum;//节点,有向边个数 int dis[maxNodeNum];//从单源点到各个点的距离 bool vis[maxNodeNum];//某个节点是否已经在队列中 int mp[maxNodeNum][maxNodeNum];//建立邻接矩阵 void SPFA(int start) { //第一步:建立队列,初始化vis,dis数组,并把源点加入队列中,修改其vis[]状态 queue<int>q; memset(vis,0,sizeof(vis)); for(int i=1;i<=nodeNum;i++) dis[i]=inf; dis[start]=0; q.push(start); vis[start]=1; //第二步:在队列中取点,把其vis状态设为0,对该点相邻的点(连接二者的边)进行松弛操作,修改相邻点的dis[] //并判断相邻的点vis[]状态是否为0(不存在于队列中),如果是,将其加入到队列中 while(!q.empty()) { int from=q.front(); q.pop(); vis[from]=0;//别忘了这一句,哎 for(int i=1;i<=nodeNum;i++) { if(dis[from]+mp[from][i]<dis[i]) { dis[i]=dis[from]+mp[from][i]; if(!vis[i])//要写在松弛成功的里面 { q.push(i); vis[i]=1; } } } } } int main()//109MS { while(cin>>nodeNum>>edgeNum&&(nodeNum||edgeNum)) { int from,to,w; memset(mp,inf,sizeof(mp));//初始化 for(int i=1;i<=edgeNum;i++)//无向图,一条无向边看为两条有向边 { cin>>from>>to>>w; if(w<mp[from][to]) mp[from][to]=mp[to][from]=w; } SPFA(1); cout<<dis[nodeNum]<<endl; } return 0; }
//floyed算法,时间复杂度高,但代码简单,可以处理负边,但图中不能包含负权回路 //可以求任意一点到另外一点的最短路,而不只是源点唯一 #include <iostream> #include <string.h> #include <queue> using namespace std; const int maxNodeNum=110;//最多节点个数 const int maxEdgeNum=10010;//最多边条数 const int inf=0x3f3f3f3f; int nodeNum,edgeNum;//节点,有向边个数 int mp[maxNodeNum][maxNodeNum];//建立邻接矩阵 void floyed() { for(int k=1;k<=nodeNum;k++) for(int i=1;i<=nodeNum;i++) for(int j=1;j<=nodeNum;j++) if(mp[i][k]+mp[k][j]<mp[i][j]) mp[i][j]=mp[i][k]+mp[k][j]; } int main()//140MS { while(cin>>nodeNum>>edgeNum&&(nodeNum||edgeNum)) { int from,to,w; memset(mp,inf,sizeof(mp));//初始化 for(int i=1;i<=edgeNum;i++)//无向图,一条无向边看为两条有向边 { cin>>from>>to>>w; if(w<mp[from][to]) mp[from][to]=mp[to][from]=w; } floyed(); cout<<mp[1][nodeNum]<<endl; } return 0; }
//dijkstra算法求最短路,单源最短路,不能处理带有负权的图 //思想为单源点加入集合,更新dis[]数组,每次取dis[]最小的那个点,加入集合,再次更新dis[]数组,取点加入集合,直到所有的点都加入集合中 #include <iostream> #include <string.h> #include <queue> using namespace std; const int maxNodeNum=110;//最多节点个数 const int maxEdgeNum=10010;//最多边条数 const int inf=0x3f3f3f3f; int nodeNum,edgeNum;//节点,有向边个数 int mp[maxNodeNum][maxNodeNum];//建立邻接矩阵 int dis[maxNodeNum];//dis[i]为源点到i的最短路径 bool vis[maxNodeNum];//判断某个节点是否已加入集合 void dijkstra(int start) { //**第一步:初始化,dis[]为最大,vis均为0(都未加入集合) memset(dis,inf,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[start]=0; //<span style="color:#ff0000;">注意不能写vis[start]=1,因为这时候第一个节点还没有被访问,下面循环中,第一个选择的就是第一个节点,切记</span> //**第二步:找dis[]值最小的点,加入集合,并更新与其相连的点的dis[]值 //一开始集合里没有任何点,下面的循环中,第一个找到的点肯定是源点 for(int i=1;i<=nodeNum;i++) { //寻找dis[]最小的点,加入集合中 int MinNumber,Min=inf;//MinNumber为dis[]值最小的点的编号 for(int j=1;j<=nodeNum;j++) { if(dis[j]<Min&&!vis[j]) { Min=dis[j]; MinNumber=j; } } //找到dis[]最小的点,加入集合,更新与其相连的点的dis值 vis[MinNumber]=1; for(int j=1;j<=nodeNum;j++) if(dis[MinNumber]+mp[MinNumber][j]<dis[j]) dis[j]=dis[MinNumber]+mp[MinNumber][j]; } } int main()//109MS { while(cin>>nodeNum>>edgeNum&&(nodeNum||edgeNum)) { int from,to,w; memset(mp,inf,sizeof(mp));//初始化 for(int i=1;i<=edgeNum;i++)//无向图,一条无向边看为两条有向边 { cin>>from>>to>>w; if(w<mp[from][to]) mp[from][to]=mp[to][from]=w; } dijkstra(1); cout<<dis[nodeNum]<<endl; } return 0; }