对于最短路,我主要使用的就是dijkstra,Floyd,SPFA这三个算法。先来介绍一下这三个算法。
1. dijkstra算法。它适用于边权为正的情况,它是单源最短路,就是从单个源点出发到所有的结点的最短路,它同时适用于无向图和有向图。
它是基于贪心的思想,看下伪代码。
1.清除所有点的标记
2.设d[0]=0,其他的d[i]=INF;
3.循环n次{
在所有的未标记的结点中,选取d值最小的结点x,
给结点x标记
对于所有从x出发的所有的边(x,y),更新d[y]=min(d[y],d[x]+w[x][y]);
}
我们用例子来看下它的流程
定义源点为0,dist[i]
为源点0到顶点i的最短路径。其过程描述如下:
第1步:从源点0开始,找到与其邻接的点:1,2,3,更新dist[]
数组,因0不与4邻接,故dist[4]
为正无穷。在dist[]
中找到最小值,其顶点为2,即此时已找到0到2的最短路。
第2步:从2开始,继续更新dist[]
数组:2与1不邻接,不更新;2与3邻接,因0→2→3
比dist[3]
大,故不更新dist[3]
;2与4邻接,因0→2→4
比dist[4]
小,故更新dist[4]
为4。
在dist[]
中找到最小值,其顶点为3,即此时又找到0到3的最短路。
第3步:从3开始,继续更新dist[]
数组:3与1邻接,因0→3→1
比dist[1]
小,更新dist[1]
为5;3与4邻接,因0→3→4
比dist[4]
大,故不更新。在dist[]
中找到最小值,其顶点为4,
即此时又找到0到4的最短路。
第4步:从4开始,继续更新dist[]
数组:4与1不邻接,不更新。在dist[]
中找到最小值,其顶点为1,即此时又找到0到1的最短路。
第5步:所有点都已找到,停止。
我们看一下例题:
A - Til the Cows Come Home POJ - 2387
Farmer John's field has N (2 <= N <= 1000) landmarks in it, uniquely numbered 1..N. Landmark 1 is the barn; the apple tree grove in which Bessie stands all day is landmark N. Cows travel in the field using T (1 <= T <= 2000) bidirectional cow-trails of various lengths between the landmarks. Bessie is not confident of her navigation ability, so she always stays on a trail from its start to its end once she starts it.
Given the trails between the landmarks, determine the minimum distance Bessie must walk to get back to the barn. It is guaranteed that some such route exists.
Input
* Lines 2..T+1: Each line describes a trail as three space-separated integers. The first two integers are the landmarks between which the trail travels. The third integer is the length of the trail, range 1..100.
Output
Sample Input
5 5
1 2 20
2 3 30
3 4 20
4 5 20
1 5 100
Sample Output
90
题意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离。这就是最短路的模板题。
#include <iostream> #include <stdio.h> #include <string> #include <string.h> #include <algorithm> #include <math.h> #include <queue> #include <vector>
#define INF 0x3f3f3f3f
using namespace std; typedef long long LL; const int maxn=1005; int T,n; int u,v,val; struct Edge { int from,to,val; Edge(int f,int t,int v):from(f),to(t),val(v){} }; struct Node { int id,w; Node(int idd,int ww):id(idd),w(ww) {} bool operator < (const Node& b) const { return w>b.w; } }; bool vis[maxn];int dis[maxn]; vector<int>G[maxn];vector<Edge>edge; void addedge(int f,int t,int val) { edge.push_back(Edge(f,t,val)); int si=edge.size(); G[f].push_back(si-1); edge.push_back(Edge(t,f,val)); si=edge.size(); G[t].push_back(si-1); } void dijkstra(int sta) { memset(vis,false,sizeof(vis)); for(int i=0;i<=n;i++) dis[i]=INF; dis[sta]=0; priority_queue<Node>q; q.push(Node(sta,0)); while(!q.empty()) { Node temp=q.top(); q.pop(); int u=temp.id; if(vis[u]) { if(u==n) break; else
continue; } vis[u]=true; int len=G[u].size(); for(int i=0;i<len;i++) { Edge e=edge[G[u][i]]; if(dis[e.to]>dis[u]+e.val) { dis[e.to]=dis[u]+e.val; q.push(Node(e.to,dis[e.to])); } } } } int main() { scanf("%d %d",&T,&n); while(T--) { scanf("%d %d %d",&u,&v,&val); addedge(u,v,val); } dijkstra(1); printf("%d\n",dis[n]); return 0; }
2. Floyd算法。如果需要求出每两点之间的最短路,不需要调用n次dijkstra算法,就可以用Floyd,但是要注意它是一个O(n^3)的算法,所以要注意使用的时候的情况,
它的核心代码就是这块:
for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
它有几个注意的细节,首先是初始化,初始化是d[i][i]=0,其他的设为INF,但是这个INF不能定义的太大,防止d[i][k]+d[k][j]溢出的问题,但是INF不能太小,防止INF真的变成最短路中的一部分,所以一般估计一下实际的最短路的上限,INF设置的比这个上限高一些就可以了。
而且有时候不关心路径的长度,只关心两点之间是否连通的时候,就是传递闭包问题,可以使用Floyd求,只要d[i][j]=min(d[i][j],d[i][k]+d[k][j]);换成d[i][j]=d[i][j] || (d[i][k]&&d[k][j]);就行意思就是i,j直接连通或者经过k两个连通,符合一种情况i,j就连通的。或者求1-n路径上经过的最大值最小,也是一样可以用Floyd(这种看情况),一般只有多次询问任意两点的最短路或者传递闭包的时候才用Floyd,因为它的时间复杂度太高了,经常爆时间,用的时候要注意。
看下例题:
B - Frogger POJ - 2253
Unfortunately Fiona's stone is out of his jump range. Therefore Freddy considers to use other stones as intermediate stops and reach her by a sequence of several small jumps.
To execute a given sequence of jumps, a frog's jump range obviously must be at least as long as the longest jump occuring in the sequence.
The frog distance (humans also call it minimax distance) between two stones therefore is defined as the minimum necessary jump range over all possible paths between the two stones.
You are given the coordinates of Freddy's stone, Fiona's stone and all other stones in the lake. Your job is to compute the frog distance between Freddy's and Fiona's stone.
Input
Output
Sample Input
2
0 0
3 4
3
17 4
19 4
18 5
0
Sample Output
Scenario #1
Frog Distance = 5.000
Scenario #2
Frog Distance = 1.414
题意:就是刚说的最大值最小的问题。先预处理出点之间的距离,然后跑Floyd就可以了,因为这题n只有200,所以没有卡时间,能过,但是C就过不了了。
#include <iostream> #include <stdio.h> #include <string> #include <string.h> #include <algorithm> #include <math.h> #include <queue> #include <vector>
#define INF 0x3f3f3f3f
using namespace std; typedef long long LL; const int maxn=1005; int n; struct Point { int x,y; }point[maxn]; double ma[maxn][maxn]; void Floyd() { for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) ma[i][j]=min(ma[i][j],max(ma[i][k],ma[k][j])); } int main() { int casee=1; while(scanf("%d",&n)!=EOF) { if(n==0) break; for(int i=1;i<=n;i++) scanf("%d %d",&point[i].x,&point[i].y); for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) { double cost=(point[i].x-point[j].x)*(point[i].x-point[j].x)+(point[i].y-point[j].y)*(point[i].y-point[j].y); cost=sqrt(cost); ma[i][j]=ma[j][i]=cost; } } Floyd(); printf("Scenario #%d\n",casee++); printf("Frog Distance = %.3f\n\n",ma[1][2]); } return 0; }
C - Heavy Transportation POJ - 1797
Hugo Heavy is happy. After the breakdown of the Cargolifter project he can now expand business. But he needs a clever man who tells him whether there really is a way from the place his customer has build his giant steel crane to the place where it is needed on which all streets can carry the weight.
Fortunately he already has a plan of the city with all streets and bridges and all the allowed weights.Unfortunately he has no idea how to find the the maximum weight capacity in order to tell his customer how heavy the crane may become. But you surely know.
Problem
You are given the plan of the city, described by the streets (with weight limits) between the crossings, which are numbered from 1 to n. Your task is to find the maximum weight that can be transported from crossing 1 (Hugo's place) to crossing n (the customer's place). You may assume that there is at least one path. All streets can be travelled in both directions.
Input
Output
Sample Input
1
3 3
1 2 3
1 3 4
2 3 5
Sample Output
Scenario #1: 4
题意:和B一样,但是这题你要是用Floyd去做就会出现这个结果
因为这题n达到1000,时间承受不了O(n^3)的时间复杂度。所以就要另辟道路了。
#include <iostream> #include <stdio.h> #include <string> #include <string.h> #include <algorithm> #include <math.h> #include <queue> #include <vector>
using namespace std; typedef long long LL; const int maxn=1005; const int INF=0x3f3f3f3f; int T,n,m; int a,b,v; struct Edge { int from,to,val; Edge(int f,int t,int v):from(f),to(t),val(v){} }; struct Node { int id,w; Node(int idd,int ww):id(idd),w(ww) {} bool operator < (const Node& b) const { return w<b.w; } }; bool vis[maxn];int dis[maxn]; vector<int>G[maxn];vector<Edge>edge; void addedge(int f,int t,int val) { edge.push_back(Edge(f,t,val)); int si=edge.size(); G[f].push_back(si-1); edge.push_back(Edge(t,f,val)); si=edge.size(); G[t].push_back(si-1); } void dijkstra(int sta) { memset(vis,false,sizeof(vis)); for(int i=0;i<=n;i++) dis[i]=0; dis[sta]=INF; priority_queue<Node>q; q.push(Node(sta,dis[sta])); while(!q.empty()) { Node temp=q.top(); q.pop(); int u=temp.id; if(vis[u]) continue; vis[u]=true; int len=G[u].size(); for(int i=0;i<len;i++) { Edge e=edge[G[u][i]]; if(dis[e.to]<min(dis[u],e.val)) { dis[e.to]=min(dis[u],e.val); q.push(Node(e.to,dis[e.to])); } } } } int main() { int casee=1; scanf("%d",&T); while(T--) { scanf("%d %d",&n,&m); for(int i=0;i<=n;i++) G[i].clear(); edge.clear(); for(int i=0;i<m;i++) { scanf("%d %d %d",&a,&b,&v); addedge(a,b,v); } dijkstra(1); printf("Scenario #%d:\n",casee++); printf("%d\n\n",dis[n]); } return 0; }
H - Cow Contest POJ - 3660
N (1 ≤ N ≤ 100) cows, conveniently numbered 1..N, are participating in a programming contest. As we all know, some cows code better than others. Each cow has a certain constant skill rating that is unique among the competitors.
The contest is conducted in several head-to-head rounds, each between two cows. If cow A has a greater skill level than cow B (1 ≤ A ≤ N; 1 ≤ B ≤ N; A ≠ B), then cow A will always beat cow B.
Farmer John is trying to rank the cows by skill level. Given a list the results of M(1 ≤ M ≤ 4,500) two-cow rounds, determine the number of cows whose ranks can be precisely determined from the results. It is guaranteed that the results of the rounds will not be contradictory.
Input
* Line 1: Two space-separated integers: N and M
* Lines 2..M+1: Each line contains two space-separated integers that describe the competitors and results (the first integer, A, is the winner) of a single round of competition: A and B
Output
* Line 1: A single integer representing the number of cows whose ranks can be determined
Sample Input
5 5
4 3
4 2
3 2
1 2
2 5
Sample Output
2
题意:有n头牛比赛,m种比赛结果,最后问你一共有多少头牛的排名被确定了,其中如果a战胜b,b战胜c,则也可以说a战胜c,即可以传递胜负。求能确定排名的牛的数目。
如果一头牛被x头牛打败,打败y头牛,且x+y=n-1,则我们容易知道这头牛的排名就被确定了,所以我们只要将任何两头牛的胜负关系确定了,在遍历所有牛判断一下是否满足x+y=n-1,将满足这个条件的牛数目加起来就是所求解。
抽象为简单的floyd传递闭包算法,在加上每个顶点的出度与入度 (出度+入度=顶点数-1,则能够确定其编号)。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue>
using namespace std; const int INF=0x3f3f3f3f; const int maxn=105; int n,m; int a,b; int ma[maxn][maxn]; void Floyd() { for(int k=1;k<=n;k++) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) ma[i][j]=ma[i][j]||(ma[i][k]&&ma[k][j]); } } } int main() { scanf("%d %d",&n,&m); memset(ma,0,sizeof(ma)); while(m--) { scanf("%d %d",&a,&b); ma[a][b]=1; } Floyd(); int ans=0; for(int i=1;i<=n;i++) { int cnt=0; for(int j=1;j<=n;j++) { if(i==j) continue; else if(ma[i][j]||ma[j][i]) cnt++; } if(cnt==n-1) ans++; } printf("%d\n",ans); return 0; }
I - Arbitrage POJ - 2240
Your job is to write a program that takes a list of currency exchange rates as input and then determines whether arbitrage is possible or not.
Input
Test cases are separated from each other by a blank line. Input is terminated by a value of zero (0) for n.
Output
Sample Input
3
USDollar
BritishPound
FrenchFranc
3
USDollar 0.5 BritishPound
BritishPound 10.0 FrenchFranc
FrenchFranc 0.21 USDollar
3
USDollar
BritishPound
FrenchFranc
6
USDollar 0.5 BritishPound
USDollar 4.9 FrenchFranc
BritishPound 10.0 FrenchFranc
BritishPound 1.99 USDollar
FrenchFranc 0.09 BritishPound
FrenchFranc 0.19 USDollar
0
Sample Output
Case 1: Yes
Case 2: No
题意:给你m种货币,给你m种货币兑换规则,问通过这些规则最后能不能盈利。eg:1美元换0.5英镑,1英镑换10法郎,1法郎换0.21美元,这样1美元能换0.5*10*0.21=1.05美元,净赚0.05美元。
思路:用Floyd找出每两种钱之间的最大兑换关系,遍历一遍,看有没有那种钱币最后能盈利,有就输出Yes,没有就是No。在处理钱币名称与编号之间的关系时,可以用map存,当然也可以用字符串比较。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <map>
using namespace std; const int INF=0x3f3f3f3f; const int maxn=55; int n,m; double r;string s1,s2; double dis[maxn][maxn]; map<string,int>ma; void Floyd() { for(int k=1;k<=n;k++) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(dis[i][j]<dis[i][k]*dis[k][j]) dis[i][j]=dis[i][k]*dis[k][j]; } } } } int main() { int casee=1; while(scanf("%d",&n)!=EOF) { if(n==0) break; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(i==j) dis[i][j]=1; else dis[i][j]=0; } } for(int i=1;i<=n;i++) { cin>>s1; ma[s1]=i; } scanf("%d",&m); for(int i=0;i<m;i++) { cin>>s1>>r>>s2; dis[ma[s1]][ma[s2]]=r; } Floyd(); bool flag=false; for(int i=1;i<=n;i++) { if(dis[i][i]>1) { flag=true; break; } } printf("Case %d: ",casee++); if(flag) printf("Yes\n"); else printf("No\n"); } return 0; }