最短路。
感觉他讲的很不错。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\n",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\n"); //坑死!!!!!! 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\n",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\n",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\n",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\n",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\n",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\n",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\n");//若没有到达终点 输出-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\n",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 */