【最短路杂题】

最短路问题是图论中的经典问题,求解单源最短路问题可以采用dijkstra算法,时间复杂度O(n^2),使用堆优化后可以达到O(nlogn)。在稀疏图中也可用spfa算法,并不比dijkstra算法表现的差。当然如果有负权值回路,dijkstra就只能GG了!求解全图中任意两点的最短路径还可以用floyd算法,时间复杂度O(n^3),虽然复杂度较高,但在需要的时候该算法也可表现的很好。求两点间的最短路径则可以通过深搜或广搜实现,时间复杂度O(m)。

当然最短路问题可以有很多变形,比如下面几道题。

Problem 1:[luogu P2384] 最短路

题目描述

给定n个点的带权有向图,求从1到n的路径中边权之积最小的简单路径。

输入输出格式

输入格式:

第一行读入两个整数n,m,表示共n个点m条边。 接下来m行,每行三个正整数x,y,z,表示点x到点y有一条边权为z的边。

输出格式:

输出仅包括一行,记为所求路径的边权之积,由于答案可能很大,因此你输出它模9987的余数即可。

输入输出样例

输入样例#1:

3 3
1 2 3
2 3 3
1 3 10

输出样例#1:

9

说明

对于20%的数据,n<=10。

对于100%的数据,n<=1000,m<=1000000。边权不超过10000。

本题是最短路径的变形,我们可以用类比法,用更新边权加和的策略来更新边权乘积,由于本题数据较水,裸spfa即可通过。

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 using namespace std;
 5
 6 int n,m,l,pre[1000005],last[1005],other[1000005];
 7 int que[1000005];
 8 long long len[1000005],d[1000005];
 9 bool vis[1000005];
10
11 void add(int u,int v,int r) {
12     pre[++l]=last[u];
13     last[u]=l;
14     other[l]=v;
15     len[l]=r;
16 }
17
18 int main() {
19     scanf("%d%d",&n,&m);
20     for (int i=1; i<=m; i++) {
21         int x,y,z;
22         scanf("%d%d%d",&x,&y,&z);
23         add(x,y,z);
24         add(y,x,z);
25     }
26     for (int i=1; i<=n; i++) d[i]=2147483647;
27     d[1]=1;
28     que[1]=1;
29     int h=0, t=1;
30     while (h<t) {
31         int cur=que[++h];
32         vis[cur]=0;
33         for (int p=last[cur]; p ;p=pre[p]) {
34             int q=other[p];
35             if (d[q]>d[cur]*len[p]) {
36                 d[q]=d[cur]*len[p];
37                 if (!vis[q]) {
38                     vis[q]=1;
39                     que[++t]=q;
40                 }
41             }
42         }
43     }
44     printf("%lld",d[n]);
45     return 0;
46 }

Problems 2:[福建省历届夏令营] 房间最短路问题 

题目描述

在一个长宽均为10,入口出口分别为(0,5)、(10,5)的房间里,有几堵墙,每堵墙上有两个缺口,求入口到出口的最短路经。

输入输出格式

输入格式:

第一排为n(n<=20),墙的数目。

接下来n排,每排5个实数x,a1,b1,a2,b2。

x表示墙的横坐标(所有墙都是竖直的),a1-b1和a2-b2之间为空缺。

a1、b1、a2、b2保持递增,x1-xn也是递增的。

输出格式:

输出最短距离,保留2位小数。

输入输出样例

输入样例#1:

2
4 2 7 8 9
7 3 4.5 6 7

输出样例#1:

10.06

本题属于比较简单的最短路建模问题。不难发现沿着墙角与墙角之间的直线走是最优的。把图中所有点都找出(墙角的点和起点终点),两两建边(当两点连线不被墙隔开时)。跑一边最短路即可求解。
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cmath>
 5 using namespace std;
 6
 7 int n,tot,l,pre[100050],last[100050],other[100050],que[100050];
 8 bool vis[100050];
 9 double x[1050],y[1050];
10 double xx[25],a[25],b[25],c[25],d[25],len[100050],dis[100050];
11
12 double minn(double x,double y) {
13     if (x>y) return y;
14     return x;
15 }
16
17 double maxx(double x,double y) {
18     if (x<y) return y;
19     return x;
20 }
21
22 void add(int u,int v,double r) {
23     pre[++l]=last[u];
24     last[u]=l;
25     other[l]=v;
26     len[l]=r;
27 }
28
29 int main() {
30     scanf("%d",&n);
31     for (int i=1; i<=n; i++) {
32         scanf("%lf %lf %lf %lf %lf",&xx[i],&a[i],&b[i],&c[i],&d[i]);
33         tot++;
34         x[tot]=xx[i];
35         y[tot]=a[i];
36         tot++;
37         x[tot]=xx[i];
38         y[tot]=b[i];
39         tot++;
40         x[tot]=xx[i];
41         y[tot]=c[i];
42         tot++;
43         x[tot]=xx[i];
44         y[tot]=d[i];
45     }
46     int s=++tot; x[s]=0;  y[s]=5;
47     int t=++tot; x[t]=10; y[t]=5;
48     for (int i=1; i<=tot; i++) {
49         for (int j=1; j<=tot; j++) {
50             if (i==j) continue;
51             if (x[i]==x[j]) {
52                 for (int k=1; k<=n; k++) {
53                     if (xx[k]==x[i]) {
54                         if ((y[i]==a[k] && y[j]==b[k]) || (y[i]==b[k] && y[j]==a[k]) || (y[i]==c[k] && y[j]==d[k]) || (y[i]==d[k] && y[j]==c[k])) {
55                             double length=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
56                             add(i,j,length);
57                             break;
58                         }
59                     }
60                 }
61                 continue;
62             }
63             bool f=0;
64             for (int k=1; k<=n; k++) {
65                 if (xx[k]<=minn(x[i],x[j])) continue;
66                 if (xx[k]>=maxx(x[i],x[j])) break;
67                 double p=(xx[k]-x[j])*(y[i]-y[j])/(x[i]-x[j])+y[j];
68                 if ((p>=0 && p<=a[k]) || (p>=b[k] && p<=c[k]) || (p>=d[k])) {
69                     f=1;
70                     break;
71                 }
72             }
73             if (!f) {
74                 double length=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
75                 add(i,j,length);
76             }
77         }
78     }
79     for (int i=1; i<=tot; i++) dis[i]=2147483637;
80     dis[s]=0;
81     que[1]=s;
82     int he=0,ta=1;
83     while (he<ta) {
84         int cur=que[++he];
85         vis[cur]=0;
86         for (int p=last[cur]; p ;p=pre[p]) {
87             int q=other[p];
88             if (dis[q]>dis[cur]+len[p]) {
89                 dis[q]=dis[cur]+len[p];
90                 if (!vis[q]) {
91                     vis[q]=1;
92                     que[++ta]=q;
93                 }
94             }
95         }
96     }
97     printf("%.2lf",dis[t]);
98     return 0;
99 }

Problem 3:[luogu P1144] 最短路计数

题目描述

给出一个N个顶点M条边的无向无权图,顶点编号为1~N。问从顶点1开始,到其他每个点的最短路有几条。

输入输出格式

输入格式:

输入第一行包含2个正整数N,M,为图的顶点数与边数。


接下来M行,每行两个正整数x, y,表示有一条顶点x连向顶点y的边,请注意可能有自环与重边。

输出格式:

输出包括N行,每行一个非负整数,第i行输出从顶点1到顶点i有多少条不同的最短路,由于答案有可能会很大,你只需要输出mod 100003后的结果即可。如果无法到达顶点i则输出0。

输入输出样例

输入样例#1:

5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5

输出样例#1:

1
1
1
2
4

说明

1到5的最短路有4条,分别为2条1-2-4-5和2条1-3-4-5(由于4-5的边有2条)。

对于20%的数据,N ≤ 100;

对于60%的数据,N ≤ 1000;

对于100%的数据,N ≤ 100000,M ≤ 200000。

本题属于求最短路方案数的问题,在能转移的状态下记下转移数,累加即可得解。本题数据较大,建议用dijkstra+堆优化。



 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include<iostream>
 5 #include<vector>
 6 #include<cstring>
 7 #include<queue>
 8 using namespace std;
 9 const int maxn=100005;
10 vector<int> G[maxn];
11 int d[maxn],ans[maxn];
12 int main() {
13     int n,m;
14     scanf("%d%d",&n,&m);
15     for(int i=0,x,y;i<m;i++) {
16         scanf("%d%d",&x,&y);
17         G[x].push_back(y);
18         G[y].push_back(x);
19     }
20     queue<int> q;
21     memset(d,-1,sizeof(d));
22     d[1]=0;
23     ans[1]=1;
24     q.push(1);
25     while(!q.empty()) {
26         int u=q.front();
27         q.pop();
28         for(int i=0;i<G[u].size();i++) {
29             int v=G[u].at(i);
30             if(d[v]==-1) {
31                 d[v]=d[u]+1;
32                 ans[v]=ans[u];
33                 q.push(v);
34             } else {
35                 if(d[v]==d[u]+1) {
36                     ans[v]+=ans[u];
37                     ans[v]%=100003;
38                 }
39             }
40         }
41     }
42     for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
43     return 0;
44 }

 
时间: 2024-10-11 00:56:05

【最短路杂题】的相关文章

【最小生成树杂题】

这里谈一下最小生成树 生成树的概念:连通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树.生成树是连通图的极小连通子图.所谓极小是指:若在树中任意增加一条边,则将出现一个回路:若去掉一条边,将会使之变成非连通图. 生成树各边的权值总和称为生成树的权.权最小的生成树称为最小生成树. 最小生成树一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边.常用于求最小生成树得算法包括kruskal(克鲁斯卡尔)算法或Prim(

HDU 5521.Meeting 最短路模板题

Meeting Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 3361    Accepted Submission(s): 1073 Problem Description Bessie and her friend Elsie decide to have a meeting. However, after Farmer Jo

_杂题_

杂题集 是个放题的好地方! **** 5.28 **** - BZOJ [3052] 糖果公园 - 据说是一道区间操作的综合题,但现在貌似蹦了? 现在还是太水,之后再来写吧. *************

[杂题]URAL1822. Hugo II&#39;s War

看懂题意的请直接跳过下一坨! 本人有表达障碍! ========================================== 题意: (题意真的很难很难懂啊!!!  去他娘的**) 有一个王国,王国里有一个国王(编号为1),他有(编号为2~n) n-1个臣子(这些臣子并不全和他有直接关系) 然后呢 国王要去打架,但是只有当他的x%个及以上的直系下属(与他有直接关系的臣子)做好打架的准备了,他才能去打架 他的直系下属也有下属,也要其中x%及以上的下属做好打架准备了,那些直系下属才会开始准备

poj1511/zoj2008 Invitation Cards(最短路模板题)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Invitation Cards Time Limit: 5 Seconds      Memory Limit: 65536 KB In the age of television, not many people attend theater performances. Antique Comedians of Malidinesia are aware of this fa

hdu 3641 数论 二分求符合条件的最小值数学杂题

http://acm.hdu.edu.cn/showproblem.php?pid=3641 学到: 1.二分求符合条件的最小值 /*==================================================== 二分查找符合条件的最小值 ======================================================*/ ll solve() { __int64 low = 0, high = INF, mid ; while(low <=

最短路入门题

http://acm.hdu.edu.cn/showproblem.php?pid=2544 DJ #include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #define N 1000001 using namespace std; int map[101][101]; int n,m; int v[101],

hdu 4961 数学杂题

http://acm.hdu.edu.cn/showproblem.php?pid=4961 先贴个O(nsqrtn)求1-n所有数的所有约数的代码: vector<int>divs[MAXN]; void caldivs() { for(int i=1;i<MAXN;i++) for(int j=i;j<MAXN;j+=i) divs[j].push_back(i); } 有了这个当时理下思路就可写了,但是重复数处理注意: 1.用一个数组vis[]  vis[i]=1表示i存在

poj 杂题 - 1959 Darts

这一题放在杂题里,是因为我没有用DP,而是使用的枚举,当然是受到了discuss里面的启发. 因为我们只能有三次机会,每一次只可以是固定的63个数,所以枚举感觉更加直观,但是不知道是不是没有DP快. #include<stdio.h> #include<string.h> int n; int Darts[63]; int main(){ int t,c=1,i,j,k,res; scanf("%d",&t); for(i = 0 ;i<=20;i