zoukankan      html  css  js  c++  java
  • 最短路。

    感觉他讲的很不错。http://www.cnblogs.com/Yan-C/p/3916281.html

    以下均从他的内容整理而来。

      1 //Bellman-ford
      2 //bellman-ford 算法解决的是一般情况下的单源最短路径问题,其边可以为负值。
      3 //bellman-ford算法可以判断图是否存在负环,若存在负环会返回一个布尔值。
      4 //当然在没有负环存在的情况下会返回所求的最短路径的值。
      5 //以hdu1874为例  http://acm.hdu.edu.cn/showproblem.php?pid=1874
      6 
      7 //     多组输入。第一行给你两个数n(代表点),m(代表边)
      8 
      9 //   第2—m+1行 ,每行三个数u,v,  w。0<=u,v<n,  w>=0;   【因为此图w>=0,所以是一定没有负环的】
     10 
     11 //   第m+2行两个数 s, t  。 s为源点,t为要到达的目的点。
     12 
     13 //   求s到t 的最短路,若存在最短路输出最短路的值,否则输出-1。
     14 /*
     15 #include<cstdio>
     16 #include<cstring>
     17 #include<iostream>
     18 #include<algorithm>
     19 using namespace std;
     20 
     21 const int maxn=9999999;
     22 int dis[210];  //保存最短路径
     23 
     24 //对于bellman-ford算法 推荐使用结构体数组存储
     25 struct node
     26 {
     27     int u,v,w;
     28 }edge[1010];
     29 
     30 
     31 int n,m,s,t;
     32 
     33 void Bellman()
     34 {
     35     bool flag=1;  //用于优化的
     36      //初始化
     37     for(int i=0;i<=n;i++)
     38         dis[i]=maxn;
     39     dis[s]=0;
     40     m=m<<1;  //此处和m = 2*m是一样的,因为建立的无向图
     41     for(int i=1;i<n;i++)
     42     {
     43         flag=0;
     44         for(int j=0;j<m;j++)
     45             if(dis[edge[j].v]>dis[edge[j].u]+edge[j].w)  //进行松弛操作
     46             {
     47                 dis[edge[j].v]=dis[edge[j].u]+edge[j].w;
     48                 flag=1;
     49             }
     50         if(!flag) break;
     51     }
     52     printf("%d
    ",dis[t]==maxn?-1:dis[t]);
     53 
     54 }
     55 int main()
     56 {
     57     while(scanf("%d%d",&n,&m)!=EOF)
     58     {
     59         for(int i=0;i<m;i++){
     60             scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
     61             edge[i+m].u=edge[i].v;  //因为为无向图所以u—>v和v—>u 是一样的
     62             edge[i+m].v=edge[i].u;
     63             edge[i+m].w=edge[i].w;
     64         }
     65         scanf("%d%d",&s,&t);
     66         Bellman();
     67     }
     68 }
     69 */
     70 
     71 //以上是用来求最短路,,下面判断负环!!
     72 //以poj3259为例  http://poj.org/problem?id=3259
     73 
     74 //题目大意: 第一行 输入一个数  是表示几组测试数据
     75 
     76 //第二行  三个数 N(点的个数),M(正边的个数),W(负边的个数) 注意 :正边为双向的,负边为单向的。
     77 
     78 //然后 M行u,v,w;
     79 
     80 //再然后W行u,v,w;
     81 
     82 //求这个图是不是存在负环。 有 YES 没NO。
     83 
     84 /*
     85 #include<cstdio>
     86 #include<algorithm>
     87 #include<cstring>
     88 using namespace std;
     89 const int maxn=520;
     90 const int maxe=5600;
     91 const int maxx=99999999;
     92 struct node
     93 {
     94     int u,v,w;
     95 }edge[maxe];
     96 
     97 int n,m,w;
     98 
     99 int dis[520];
    100 int Bellman ()
    101 {
    102     int flag=0;
    103     for(int i=0;i<=n;i++)
    104         dis[i]=maxx;
    105     dis[1]=0;
    106     int i;
    107     for( i=0;i<n;i++)   //注意,,此处i从零开始,若没有负环i不会增到n-1;
    108     {
    109           flag=0;
    110         for(int j=0;j<m;j++)
    111         {
    112 
    113         if(dis[edge[j].v]>dis[edge[j].u]+edge[j].w)
    114             {
    115                 dis[edge[j].v]=dis[edge[j].u]+edge[j].w;
    116                 flag=1;
    117             }
    118 
    119         }
    120         if(!flag) break;
    121          if(i==n-1) return 1;
    122     }
    123     //if(i==n-1) printf("YES
    ");   //坑死!!!!!!
    124       //  else puts("NO");
    125         return 0;
    126 }
    127 
    128 int main()
    129 {
    130     int t;
    131     scanf("%d",&t);
    132     while(t--)
    133     {
    134         scanf("%d%d%d",&n,&m,&w);
    135         for(int i=0;i<m;i++)
    136         {
    137             scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
    138             edge[i+m].u=edge[i].v;
    139             edge[i+m].v=edge[i].u;
    140             edge[i+m].w=edge[i].w;
    141         }
    142         m=m<<1;
    143         for(int i=m;i<w+m;i++)
    144             {
    145                 scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
    146                 edge[i].w=-edge[i].w;
    147             }
    148             m+=w;
    149        if( Bellman()) puts("YES");
    150         else puts("NO");
    151     }
    152     return 0;
    153 }
    154 
    155 */
    156 
    157 //Dijkstra算法
    158 //hdu1874
    159 //朴素实现
    160 /*
    161 #include<iostream>
    162 #include<cmath>
    163 #include<cstdio>
    164 #include<algorithm>
    165 #include<cstring>
    166 using namespace std;
    167 const int N=200,M=1000;
    168 int maxn=100000000;
    169 int w[N][N],d[M];  //边权,最小数
    170 int vis[N];//标记
    171 int x,y,c,m,n,s,t;
    172 int Dijkstra(int s,int t)
    173 {
    174 memset(vis,0,sizeof(vis));   //初始标记
    175 for(int i=0;i<n;i++) d[i]=(i==s?0:maxn);  //初始s点距离为0,原点到其它点距离为无穷大
    176     for(int i=0;i<n;i++)  {
    177 int x,m=maxn;
    178         for(int y=0;y<n;y++) if(!vis[y]&&d[y]<=m)  m=d[x=y];  //搜索上次的最短点到下一距离最短的未访问点,并把该点记为x;
    179         vis[x]=1;//标记该店已访问
    180         for(int y=0;y<n;y++) d[y]=min(d[y],d[x]+w[x][y]);//计算x到任意点y的最小距离:……或者上一点加边权
    181     }
    182     if(d[t]==maxn)return -1;
    183     else return d[t];
    184 }
    185 
    186 int main()
    187 {
    188     while(scanf("%d",&n)==1&&n)
    189     { for(int i=0;i<n;i++)
    190     for(int j=0;j<n;j++)            // memset(w,0,sizeof(w);
    191         w[i][j]=maxn;
    192         scanf("%d",&m);
    193         for(int i=0;i<m;i++){
    194             scanf("%d%d%d",&x,&y,&c);
    195             w[x][y]=(w[x][y]==maxn?c:min(c,w[x][y]));
    196             w[y][x]=w[x][y];
    197         }
    198         scanf("%d%d",&s,&t);
    199         printf("%d
    ",Dijkstra(s,t));
    200     }
    201     return 0;
    202 }
    203 */
    204 
    205 //Dijkstra算法
    206 //用链式前向星和优先队列实现
    207 /*
    208 #include <cstdio>
    209 #include <cstring>
    210 #include <algorithm>
    211 #include <iostream>
    212 #include <queue>
    213 #define MAX 9999999
    214 
    215 using namespace std;
    216 //pair 的first 保存的为最短距离, second保存的为顶点编号
    217 typedef pair<int, int >P;//对组  不知道请自行百度
    218 
    219 struct node
    220 {
    221     int v, w;//v 为到达的点, w为权重
    222     int next;//记录下一个结构体的位置 ,就向链表的next功能是一样的
    223 };
    224 node edge[2003];//存所有的边,因为是无向图,所以*2
    225 int cnt;//结构体的下标
    226 int n, s, t;//n 点数,s 起点,t止点
    227 int head[203];//和链表的头指针数组是一样的。只不过此处head[u]记录的为最后加入 edge 的且与u相连的边在 edge 中的位置,即下标
    228 
    229 void add(int u, int v, int w)//加边操作
    230 {
    231     edge[cnt].v = v;
    232     edge[cnt].w = w;
    233     edge[cnt].next = head[u];//获得下一个结构体的位置
    234     head[u] = cnt++;//记录头指针的下标
    235 }
    236 
    237 void dijkstra()
    238 {
    239     int dis[203];//最短路径数组
    240     int i, v;//v保存从队列中取出的数的第二个数  也就是 顶点的编号
    241     priority_queue<P,vector<P>,greater<P> >que;//优先队列 从小到大
    242     node e;//保存边的信息,为了书写方便
    243     P p;//保存从队列取出的数值
    244 
    245     fill(dis,dis+n,MAX);//初始化,都为无穷大
    246     dis[s] = 0;//s—>s  距离为0
    247     que.push(P(0,s));//放入距离 为0   点为s
    248     while(!que.empty()){
    249         p = que.top();//取出队列中最短距离最小的对组
    250         que.pop();//删除
    251         v = p.second;//获得最短距离最小的顶点编号
    252         if(dis[v] < p.first)//若取出的不是最短距离
    253             continue;//则进行下一次循环
    254         for(i=head[v];i!=-1;i=edge[i].next)//对与此点相连的所有的点进行遍历
    255         {
    256             e = edge[i];//为了书写的方便。
    257             if(dis[e.v]>dis[v]+e.w){//进行松弛
    258                 dis[e.v]=dis[v]+e.w;//松弛成功
    259                 que.push(P(dis[e.v],e.v));//讲找到的松弛成功的距离 和顶点放入队列
    260             }
    261         }
    262     }
    263     printf("%d
    ",dis[t]==MAX?-1:dis[t]);//输出结果
    264 }
    265 
    266 int main()
    267 {
    268     int m, u, v, w;
    269 
    270     while(scanf("%d %d",&n,&m)==2){//获取点数  边数
    271         cnt = 0;//结构体下标从0开始
    272         memset(head,-1,sizeof(head));//初始化head[N]数组
    273         while(m--){
    274             scanf("%d %d %d",&u,&v,&w);//获取u,v,w(u,v)
    275             add(u,v,w);//加边
    276             add(v,u,w);//加边
    277         }
    278         scanf("%d %d",&s,&t);//获取起止点
    279         dijkstra();
    280     }
    281     return 0;
    282 }
    283 
    284 */
    285 
    286 
    287 //floyd-Warshall算法   时间复杂度较高O(n^3)
    288 //  k i j  或者 i k j   不要写成 i j  k  和内存读取有关
    289 /*
    290 #include<cstdio>
    291 #include<cstring>
    292 #include<algorithm>
    293 #include<cmath>
    294 using namespace std;
    295 int g[210][210];
    296 int n,m,s,t;
    297 const int maxn=99999999;
    298 void floyd ()
    299 {
    300 
    301     for(int k=0;k<n;k++)
    302         for(int i=0;i<n;i++)
    303             for(int j=0;j<n;j++)
    304             g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
    305       printf("%d
    ",g[s][t]==maxn?-1:g[s][t]);
    306 }
    307 
    308 int main()
    309 {
    310     int x,y,c;
    311     while(scanf("%d%d",&n,&m)!=EOF)
    312     {
    313          for(int i=0;i<n;i++)
    314             for(int j=0;j<n;j++)
    315                 g[i][j]=(i==j?0:maxn);
    316         for(int i=0;i<m;i++)
    317             {
    318                 scanf("%d%d%d",&x,&y,&c);
    319                 g[y][x]=g[x][y]=(g[x][y]>c?c:g[x][y]);
    320             }
    321             scanf("%d%d",&s,&t);
    322         floyd();
    323     }
    324     return 0;
    325 }
    326 */
    327 
    328 //SPFA算法   (bellman-ford算法的优化)
    329 
    330 //朴素实现
    331 /*
    332 #include <cstdio>
    333 #include <cstring>
    334 #include <algorithm>
    335 #include <iostream>
    336 #include <queue>
    337 #define MAX 9999999
    338 
    339 using namespace std;
    340 
    341 int G[503][503];
    342 int n;
    343 
    344 bool SPFA()
    345 {
    346     int u;//从队列Q中取出的数
    347     int i;
    348     queue<int >que;//Q队列
    349     int dis[503];//保存最短距离
    350     bool vis[503];//访问数组
    351     int flag[503];//记录点进入队列的次数
    352 
    353     memset(flag,0,sizeof(flag));//初始化为0
    354     memset(vis,false,sizeof(vis));//初始化
    355     fill(dis,dis+n+1,MAX);//初始化
    356     dis[1] = 0;//从  1 开始
    357     que.push(1);//将 1 放入队列
    358     while(!que.empty()){//Q 不为空
    359         u = que.front();//从Q中取出一个数
    360         que.pop();//删除此数
    361         vis[u] = false;//标记为 未 访问过
    362         for(i=1;i<=n;i++)//对所有的边
    363         {
    364             if(dis[i]>dis[u]+G[u][i]){//进行松弛
    365                 dis[i] = dis[u]+G[u][i];//松弛成功
    366                 if(!vis[i]){//若点i 未被访问过
    367                     vis[i] = true;//标记为访问过
    368                     flag[i]++;//入队列次数+1
    369                     if(flag[i]>=n)//若此点进入队列此数超过n次  说明有负环
    370                         return true;//有负环
    371                     que.push(i);//将 此点放入队列
    372                 }
    373             }
    374         }
    375     }
    376     return false;//没有负环
    377 }
    378 
    379 int main()
    380 {
    381     int t, k, i, j, u, v, w, m;
    382 
    383     scanf("%d",&t);
    384     while(t--){
    385         scanf("%d %d %d",&n,&m,&k);
    386         for(i=1;i<=n;i++)
    387             for(j=1;j<=n;j++)
    388                 G[i][j] = i==j?0:MAX;
    389         for(i=0;i<m;i++)
    390         {
    391             scanf("%d %d %d",&u,&v,&w);
    392             if(G[u][v]>w)
    393                 G[u][v] = G[v][u] = w;
    394         }
    395         for(i=0;i<k;i++)
    396         {
    397             scanf("%d %d %d",&u,&v,&w);
    398             G[u][v] = -w;
    399         }
    400         printf("%s
    ",SPFA()?"YES":"NO");
    401     }
    402     return 0;
    403 }
    404 
    405 */
    406 
    407 //SPFA算法对于稀疏图才能发挥它的大作用,对于稀疏图用  前向星
    408 //下面就是 SPFA+前向星的程序  并应用了SLF  双向队列进行优化
    409 
    410 /*
    411 #include <cstdio>
    412 #include <cstring>
    413 #include <algorithm>
    414 #include <iostream>
    415 #include <queue>
    416 #define MAX 9999999
    417 
    418 using namespace std;
    419 
    420 struct node//前向星
    421 {
    422     int v,w;//v 终点,w 权值
    423     int next;//下一个
    424 };
    425 node edge[5203];//前向星
    426 int head[503];//头指针式的数组
    427 int cnt;//下标
    428 int n;//点的个数
    429 
    430 void add(int u, int v, int w)//加边  建议 若看不懂前向星 请自己动手画一下 按照给出的数据和程序
    431 {
    432     edge[cnt].v = v;//
    433     edge[cnt].w = w;//
    434     edge[cnt].next = head[u];//
    435     head[u] = cnt++;//
    436 }
    437 
    438 bool SPFA()
    439 {
    440     int i, u, v;//u 从Q中取出的点  v找到的点
    441     int dis[503];//保存最短路径
    442     int flag[503];//保存某点加入队列的次数
    443     bool vis[503];//标记数组
    444     deque<int>que;//双向队列  自己百度
    445 
    446     fill(dis,dis+n+1,MAX);//初始化
    447     memset(flag,0,sizeof(flag));//初始化
    448     memset(vis,false,sizeof(vis));//初始化
    449     dis[1] = 0;// s为1
    450     que.push_back(1);//将s = 1 加入队列
    451     while(!que.empty()){//当队列不为空
    452         u = que.front();//从队列中取出一个数
    453         que.pop_front();//删除
    454         vis[u] = false;//标记为未访问
    455         for(i=head[u];i!=-1;i=edge[i].next)//对所有与该边相连的边进行查找
    456         {
    457             v = edge[i].v;//保存点 便于操作
    458             if(dis[v]>dis[u]+edge[i].w){//进行松弛操作
    459                 dis[v] = dis[u]+edge[i].w;//松弛成功
    460                 if(!vis[v]){//若该点未被标记
    461                     vis[v] = true;//标记为真
    462                     flag[v]++;//该点入队列次数++
    463                     if(flag[v]>=n)//若该点进入队列次数超过n次 说明有负环
    464                         return true;//返回有负环
    465                     //一下为SLF优化
    466                     if(!que.empty() && dis[v]<dis[que.front()])//若为队列不为空 && 队列第一个点的最短距离大于当前点的最短距离
    467                         que.push_front(v);//将该点放到队首
    468                     else//不然
    469                         que.push_back(v);//放入队尾
    470                 }
    471             }
    472         }
    473     }
    474     return false;//没有负环
    475 }
    476 
    477 int main()
    478 {
    479     int u, v, w, m, k, t;
    480 
    481     scanf("%d",&t);//获取测试数据
    482     while(t--){
    483         memset(head,-1,sizeof(head));//初始化
    484         cnt = 0;//下标为0  初始化
    485         scanf("%d %d %d",&n,&m,&k);//获取点的个数 ,正边的个数, 负边的个数
    486         while(m--){
    487             scanf("%d %d %d",&u,&v,&w);//正边获取
    488             add(u,v,w);//无向图
    489             add(v,u,w);//双向建图
    490         }
    491         while(k--){
    492             scanf("%d %d %d",&u,&v,&w);
    493             add(u,v,-w);//单向图
    494         }
    495         printf("%s
    ",SPFA()?"YES":"NO");//输出结果
    496     }
    497     return 0;
    498 }
    499 
    500 */
    501 
    502 
    503 // BFS 求解最短路
    504 //hdu1874
    505 /*
    506 #include <algorithm>
    507 #include <iostream>
    508 #include <cstdio>
    509 #include <cstring>
    510 #include <queue>
    511 
    512 using namespace std;
    513 
    514 struct P
    515 {
    516     int v, w;//v 顶点 w 最短距离
    517     bool operator <(const P &a)const{
    518         return a.w < w;//按w  从小到大排序
    519     }
    520 };
    521 struct node//前向星
    522 {
    523     int v, w;//v 顶点  w权重
    524     int next;//下一个位置
    525 };
    526 node edge[2003];
    527 int head[203];//头指针数组
    528 int cnt, s, t;// cnt 下标
    529 
    530 void add(int u, int v, int w)//加边操作
    531 {
    532     edge[cnt].v = v;
    533     edge[cnt].w = w;
    534     edge[cnt].next = head[u];
    535     head[u] = cnt++;
    536 }
    537 
    538 void BFS()
    539 {
    540     priority_queue<P>que;//优先队列   按w从小到大
    541     bool vis[203];//标记数组, 标记是否被访问过
    542     P p, q;
    543     int i, v;
    544 
    545     memset(vis,false,sizeof(vis));//初始化
    546     p.v = s;//顶点为 s
    547     p.w = 0;//距离为 0
    548     que.push(p);//放入队列
    549     while(que.empty() == false){//队列不为空
    550         p = que.top();//取出队列的队首
    551         que.pop();//删除
    552         if(p.v == t){//若找到终点
    553             printf("%d
    ",p.w);//输出结果
    554             return ;//返回
    555         }
    556         vis[p.v] = true;//此点标记为访问过
    557         for(i=head[p.v];i!=-1;i=edge[i].next)//查找与该点相连的点
    558         {
    559             v = edge[i].v;
    560             if(vis[v] == false){//若点未被访问过
    561                 q.v = v;//存入结构体
    562                 q.w = p.w+edge[i].w;//距离更新
    563                 que.push(q);//放入队列
    564             }
    565         }
    566     }
    567     printf("-1
    ");//若没有到达终点  输出-1
    568 }
    569 
    570 int main()
    571 {
    572     int m, u, v, w, n;
    573 
    574     while(scanf("%d %d",&n,&m)==2){//获取点的个数  边的个数
    575         memset(head,-1,sizeof(head));//初始化
    576         cnt = 0;//初始化
    577         while(m--){
    578             scanf("%d %d %d",&u,&v,&w);//获取u,v,w
    579             add(u,v,w);//加边
    580             add(v,u,w);//无向图   双向加边
    581         }
    582         scanf("%d %d",&s,&t);//获取起止点
    583         BFS();
    584     }
    585     return 0;
    586 }
    587 
    588 */
    589 
    590 // 路径还原
    591 /*
    592 #include <algorithm>
    593 #include <iostream>
    594 #include <cstdio>
    595 #include <cstring>
    596 #include <queue>
    597 #define INF 999999
    598 using namespace std;
    599 
    600 int G[203][203];
    601 int n, s, t;
    602 
    603 bool Dijkstra()
    604 {
    605     int i, j, k;
    606     int dis[203];
    607     bool vis[203];
    608     int pre[203];//记录路径
    609 
    610     memset(vis,false,sizeof(vis));
    611     fill(dis,dis+n,INF);
    612     memset(pre,-1,sizeof(pre));//初始化为-1
    613     dis[s] = 0;
    614     for(i=0;i<n;i++)
    615     {
    616         k = -1;
    617         for(j=0;j<n;j++)
    618             if(vis[j] == false && (k == -1 || dis[k]>dis[j]))
    619                 k = j;
    620         if(k == -1)
    621             break;
    622         vis[k] = true;
    623         for(j=0;j<n;j++)
    624             if(vis[j] == false && dis[j]>dis[k]+G[k][j]){
    625                 dis[j] = dis[k]+G[k][j];
    626                 pre[j] = k;//在松弛操作中更新边的同时  记录路径!!!!!!!!!!!!!!
    627             }
    628     }
    629     printf("s——>t  的最短路为 :");
    630     printf("%d
    ",dis[t]);
    631     printf("路径为 :");
    632     //一下部分为路径的还原
    633     queue<int >que;//申请一个队列
    634     for(t;t!=-1;t=pre[t])//从t 一直寻找到s
    635         que.push(t);//放入队列
    636     printf("%d",que.front());//输出第一个
    637     que.pop();//删除
    638     while(!que.empty()){//队列不为空
    639         printf("——>%d",que.front());//输出
    640         que.pop();//删除
    641     }
    642 }
    643 
    644 int main()
    645 {
    646     int i, j, u, v, w, m;
    647 
    648     while(scanf("%d %d",&n,&m)==2){
    649         for(i=0;i<n;i++)
    650             for(j=0;j<n;j++)
    651                 G[i][j] = i==j?0:INF;
    652         while(m--){
    653             scanf("%d %d %d",&u,&v,&w);
    654             if(G[u][v]>w)
    655                 G[u][v] = G[v][u] = w;
    656         }
    657         scanf("%d %d",&s,&t);
    658         Dijkstra();
    659     }
    660     return 0;
    661 }
    662 */
  • 相关阅读:
    html中滚动条的样式
    在个人机上发布web项目
    Apache与SVN的集成
    待完成
    chmod
    【转】ubuntu修改IP地址和网关的方法
    ubuntu 添加svn服务
    生成指定大小的空文件
    数码单反相机完全攻略
    【转】ubuntu subversion安装
  • 原文地址:https://www.cnblogs.com/yijiull/p/6615820.html
Copyright © 2011-2022 走看看