博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
图论算法——最短路系列
阅读量:4560 次
发布时间:2019-06-08

本文共 11214 字,大约阅读时间需要 37 分钟。

最短路经典算法整理

1.弗洛伊德:三重循环

例题:

codevs

1020 孪生蜘蛛

 

时间限制: 1 s
空间限制: 128000 KB
题目等级 : 黄金 Gold
 
 
 
 
题目描述
Description

在G城保卫战中,超级孪生蜘蛛Phantom001和Phantom002作为第三层防卫被派往守护内城南端一带极为隐秘的通道。

根据防护中心的消息,敌方已经有一只特种飞蛾避过第二层防卫,直逼内城南端通道入口。但优秀的蜘蛛已经在每个通道内埋下了坚固的大网,无论飞蛾进入哪个通道,他只有死路一条!(因为他是无法挣脱超级蛛网的)

现在,001和002分别驻扎在某两个通道内。各通道通过内线相通,通过每条内线需要一定的时间。当特种飞蛾被困某处,001或002会迅速赶来把它结果掉(当然是耗时最少的那个)。

001跟002都想尽早的完成任务,他们希望选择在最坏情况下能尽早完成任务的方案。

 

输入描述
Input Description

第一行为一个整数N (N<=100) 表示通道数目。

接下来若干行每行三个正整数a,b,t 表示通道a,b有内线相连,通过的时间为t。(t<=100)

(输入保证每个通道都直接/间接连通)

输出描述
Output Description

两个不同的整数x1,x2,分别为001,002驻扎的地点。(如果有多解,请输出x1最小的方案,x1相同则输出x2最小的方案)

样例输入
Sample Input

3

1 2 5

2 3 10

3 1 3

样例输出
Sample Output

1 2

数据范围及提示 Data Size & Hint

1 #include 
2 #include
3 #include
4 #include
5 #define inf 0x7fffffff 6 using namespace std; 7 int d[110][110],b[110][110],id; 8 int main (){ 9 int n,a,b1,t;10 cin>>n;11 for (int i=0;i<=n;i++)12 for (int j=0;j<=n;j++)13 if (i==j) d[i][j]=0;14 else d[i][j]=inf;15 while(scanf("%d%d%d",&a,&b1,&t)!=EOF)16 d[a][b1]=d[b1][a]=t;17 18 for (int k=1;k<=n;k++)19 for (int i=1;i<=n;i++)20 for (int j=1;j<=n;j++)21 if (d[i][k]
b[i][j])Min=b[i][j],p=i,q=j;35 }36 cout<

<<" "<

上述算法注意用弗洛伊德处理最坏情况;

2  Dijkstra

1 设起点为s,dis[v]表示从s到v的最短路径,pre[v]为v的前驱节点,用来输出路径。 2        a)初始化:dis[v]=∞(v≠s); dis[s]=0; pre[s]=0;  3        b)For (i = 1; i <= n ; i++) 4             1.在没有被访问过的点中找一个顶点u使得dis[u]是最小的。 5             2.u标记为已确定最短路径 6             3.For 与u相连的每个未确定最短路径的顶点v 7               if  (dis[u]+w[u][v] < dis[v])  8                { 9                   dis[v] = dis[u] + w[u][v];10                   pre[v] = u;11                }12         c)算法结束:dis[v]为s到v的最短距离;pre[v]为v的前驱节点,用来输出路径。

例题:

最小花费

最小花费
【问题描述】
    在n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定
这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A最少需要多少钱使得转账后B收
到100元。
【输入格式】
    第一行输入两个正整数n,m,分别表示总人数和可以互相转账的人的对数。
    以下m行每行输入三个正整数x,y,z,表示标号为x的人和标号为y的人之间互相转账需要扣除z%
的手续费 (z<100)。
    最后一行输入两个正整数A,B。数据保证A与B之间可以直接或间接地转账。
【输出格式】
    输出A使得B到账100元最少需要的总费用。精确到小数点后8位。
【输入样例】
    3 3
    1 2 1
    2 3 2
    1 3 3
    1 3
【输出样例】
    103.07153164
【数据规模】
    1<=n<=2000
1 #include
2 using namespace std; 3 double a[2001][2001],dis[2001]= {
0},minn; 4 int n,m,i,j,k,x,y,f[2001]= {
0}; 5 void init() { 6 cin>>n>>m; 7 for(i=1; i<=m; i++) { 8 scanf("%d%d",&j,&k); 9 scanf("%lf",&a[j][k]);10 a[j][k]=(100-a[j][k])/100;11 a[k][j]=a[j][k];12 }13 cin>>x>>y;14 }15 void dijkstra(int x) {16 for(i=1; i<=n; i++)dis[i]=a[x][i];17 dis[x]=1;18 f[x]=1;19 for(i=1; i<=n-1; i++) {20 minn=0;21 for(j=1; j<=n; j++)22 if(f[j]==0&&dis[j]>minn) {23 k=j;24 minn=dis[j];25 }26 f[k]=1;27 if(k==y)break;28 for(j=1; j<=n; j++)29 if(f[j]==0&&dis[k]*a[k][j]>dis[j])dis[j]=dis[k]*a[k][j]
}31 }32 int main() {33     init();34     dijkstra(x);35     printf("%0.8lf",100/dis[y]);36     return 0;37 }

3 Bellman

此算法性能较差,不经常用;

1 设s为起点,dis[v]即为s到v的最短距离,pre[v]为v前驱。w[j]是边j的长度,且j连接u、v。2 初始化:dis[s]=0,dis[v]=∞(v≠s),pre[s]=03 For (i = 1; i <= n-1; i++)4 For (j = 1; j <= E; j++)         //注意要枚举所有边,不能枚举点。5    if (dis[u]+w[j]

不做过多介绍;

4 spfa

1   dis[i]记录从起点s到i的最短路径,w[i][j]记录连接i,j的边的长度。pre[v]记录前趋。 2     team[1..n]为队列,头指针head,尾指针tail。 3     布尔数组exist[1..n]记录一个点是否现在存在在队列中。 4     初始化:dis[s]=0,dis[v]=∞(v≠s),memset(exist,false,sizeof(exist)); 5     起点入队team[1]=s; head=0; tail=1;exist[s]=true; 6     do 7     {  8     1、头指针向下移一位,取出指向的点u。 9     2、exist[u]=false;已被取出了队列10     3、for与u相连的所有点v          //注意不要去枚举所有点,用数组模拟邻接表存储11               if (dis[v]>dis[u]+w[u][v])12                  {13                       dis[v]=dis[u]+w[u][v];14                       pre[v]=u;15                      if (!exist[v]) //队列中不存在v点,v入队。16                        {17                                     //尾指针下移一位,v入队;18                                   exist[v]=true;19                        }20                  }21     }22     while (head < tail);

例题;

香甜的黄油
(Sweet Butter
)
【问题描述】
农夫
John
发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道
N
1<=N<=500
)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜
黄油。当然,他将付出额外的费用在奶牛上。
农夫
John
很狡猾。像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。他打算将糖放在那里然后下午发出铃声,以至他
可以在晚上挤奶。
农夫
John
知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场
(他将把糖放在那)。
【输入格式】
butter.in
第一行
:
三个数:奶牛数
N
,牧场数
P
2<=P<=800
),牧场间道路数
C(1<=C<=1450)
第二行到第
N+1
: 1
N
头奶牛所在的牧场号。
N+2
行到第
N+C+1
行:每行有三个数:相连的牧场
A
B
,两牧场间距(
1<=D<=255
),当然,连接是双向的。
【输出格式】
butter.out
一行
输出奶牛必须行走的最小的距离和
.
【输入样例】
3 4 5
2
3
4
1 2 1
1 3 5
2 3 7
2 4 3
3 4 5
样例图形
         P2 
P1 @--1--@ C1
    \    |\
     \   | \
      5  7  3
       \ |   \
        \|    \ C3
      C2 @--5--@
         P3    P4
【输出样例】
8
             //说明:放在
4
号牧场最优

代码;

1 #include
2 #include
3 #include
4 using namespace std; 5 int n,p,c,i,j,x,y,t,min1,head,tail,tot,u; 6 int a[801][801],b[501],dis[801],num[801],w[801][801],team[1601]; 7 bool exist[801]; 8 int main() { 9 freopen("butter.in","r",stdin);10 freopen("butter.out","w",stdout);11 cin>>n>>p>>c;12 for(i=1; i<=p; i++) {13 b[i]=0;14 num[i]=0;15 for(j=1; j<=p; j++)16 w[i][j]=0x7fffffff/3;17 }18 for(i=1; i<=n; i++)19 cin>>b[i];20 for(i=1; i<=c; i++) { //邻接矩阵存储21 cin>>x>>y>>t;22 w[x][y]=t;23 a[x][++num[x]]=y;24 a[y][++num[y]]=x;25 w[y][x]=w[x][y];26 }27 min1=0x7fffffff/3;28 for(i=1; i<=p; i++) {29 for(j=1; j<=p; j++) dis[j]=0x7fffffff/3;30 memset(team,0,sizeof(team)); //队列数组初始化31 memset(exist,false,sizeof(exist)); //exist标志初始化32 dis[i]=0;33 team[1]=i;34 head=0;35 tail=1;36 exist[i]=true; //起始点入队37 do {38 head++;39 head=((head-1)%1601)+1; //循环队列处理40 u=team[head];41 exist[u]=false;42 for(j=1; j<=num[u]; j++)43 if (dis[a[u][j]]>dis[u]+w[u][a[u][j]]) {44 dis[a[u][j]]=dis[u]+w[u][a[u][j]];45 if (!exist[a[u][j]]) {46 tail++;47 tail=((tail-1)%1601)+1;48 team[tail]=a[u][j];49 exist[a[u][j]]=true;50 }51 }52 } while(head!=tail);53 tot=0;54 for(j=1; j<=n; j++)55 tot+=dis[b[j]];56 if (tot

5  并查集

普遍代码:

1 #include
2 #include
3 using namespace std; 4 #define maxn 20001 5 int father[maxn]; 6   int m,n,i,x,y,q; 7   /* 8   int find(int x) //用非递归的实现 9   {10    while (father[x] != x) x= father[x];11    return x;12   }13   */14   int find(int x) //用递归的实现15    {16    if (father[x] != x) father[x] = find(father[x]); //路径压缩17    return father[x];18   19 }20   void unionn(int r1,int r2)21    {22    father[r2] = r1;23   24 }25   26 int main()27    {28    freopen("relation.in","r",stdin);29    freopen("relation.out","w",stdout);30    cin >> n >> m;31    for (i = 1; i <= n; i++)32    father[i] = i; //建立新的集合,其仅有的成员是i33    for (i = 1; i <= m; i++)34    {35    scanf("%d%d",&x,&y);36    int r1 = find(x);37    int r2 = find(y);38    if (r1 != r2) unionn(r1,r2);39   40 }41    cin >> q;42    for (i = 1; i <= q; i++)43    {44    scanf("%d%d",&x,&y);;45    if (find(x) == find(y)) printf("Yes\n") else printf("No\n");    } return 0;50   51 }

经典例题:家谱树 codevs

6 prim  经典代码

算法采用与Dijkstra、Bellman-Ford算法一样的“蓝白点”思想:白点代表已经进入最小生成树的点,蓝点代表未进入最小生成树的点。算法描述:以1为起点生成最小生成树,min[v]表示蓝点v与白点相连的最小边权。MST表示最小生成树的权值之和。a)初始化:min[v]= ∞(v≠1); min[1]=0;MST=0;b)for (i = 1; i<= n; i++)1.寻找min[u]最小的蓝点u。2.将u标记为白点3.MST+=min[u]4.for 与白点u相连的所有蓝点v                   if (w[u][v]
1 #include
2 #include
3 #include
4 using namespace std; 5 int g[101][101]; //邻接矩阵 6 int minn[101]; //minn[i]存放蓝点i与白点相连的最小边权 7 bool u[101]; //u[i]=True,表示顶点i还未加入到生成树中 8 //u[i]=False,表示顶点i已加入到生成树中 9 int n,i,j;10 int main()11 {12 freopen("wire.in","r",stdin);13 freopen("wire.out","w",stdout);14 cin >> n;15 for (i = 1; i <= n; i++)16 for (j = 1; j <= n; j++)17 cin >> g[i][j]; 18 memset(minn,0x7f,sizeof(minn)); //初始化为maxint19 minn[1] = 0;20 memset(u,1,sizeof(u)); //初始化为True,表示所有顶点为蓝点21 for (i = 1; i <= n; i++)22 {23 int k = 0;24 for (j = 1; j <= n; j++) //找一个与白点相连的权值最小的蓝点k25 if (u[j] && (minn[j] < minn[k]))26 k = j;27 u[k] = false; //蓝点k加入生成树,标记为白点28 for (j = 1; j <= n; j++) //修改与k相连的所有蓝点29 if (u[j] && (g[k][j] < minn[j]))30 minn[j] = g[k][j]; 31 } 32 int total = 0;33 for (i = 1; i <= n; i++) //累加权值 34 total += minn[i];35 cout << total << endl;36 return 0;37 }

7 克鲁斯卡尔

1 算法描述: 2 初始化并查集。father[x]=x。 3 tot=0 4 将所有边用快排从小到大排序。 5 计数器 k=0; 6 for (i=1; i<=M; i++)      //循环所有已从小到大排序的边 7   if  这是一条u,v不属于同一集合的边(u,v)(因为已经排序,所以必为最小), 8     begin 9      ①合并u,v所在的集合,相当于把边(u,v)加入最小生成树。10      ②tot=tot+W(u,v)11       ③k++12       ④如果k=n-1,说明最小生成树已经生成,则break; 13     end;14 6. 结束,tot即为最小生成树的总权值之和。*/

经典代码

1 #include
2 #include
3 #include
4 using namespace std; 5 struct k{ 6 int x,y,v; 7 8 }a[10000]; 9 int fa[1000];10 int find(int x)11 {12 if(fa[x]!=x)13 {fa[x]=find(fa[x]);14 return fa[x];15 }16 else17 return x;18 }19 void un(int x,int y)20 {21 fa[x]=y;22 }23 int cmp( k a, k b) 24 {25 if (a.v < b.v) return 1;26 else return 0; 27 }28 int main()29 {30 int s,m=0;31 int n;32 cin>>n;33 for(int i=1;i<=n;i++)34 {35 for(int j=1;j<=n;j++)36 {37 cin>>s;38 if(s!=0)39 {40 m++;41 a[m].x=i;42 a[m].y=j;43 a[m].v=s;44 }45 }46 }47 for(int i=1;i<=n;i++)48 {49 fa[i]=i;50 }51 sort(a+1,a+m+1,cmp);52 int k=0,tot=0;53 for(int i=1;i<=m;i++)54 {55 int r=find(a[i].x);56 int rr=find(a[i].y);57 if(r!=rr)58 {59 un(r,rr);60 tot+=a[i].v;61 k++;62 }63 if(k==n-1)64 {65 break;66 }67 }68 cout<

 

转载于:https://www.cnblogs.com/lyqlyq/p/6736043.html

你可能感兴趣的文章
Mongodb 安装
查看>>
WPF窗口贴边隐藏(类似QQ)
查看>>
VS2008无法切换到视图设计器
查看>>
爱情故事:追忆似水流年 回味永恒的爱恋
查看>>
android mvn android:deploy签名问题
查看>>
transient
查看>>
[HDU 4348]To the moon
查看>>
初识【Windows API】--文本去重
查看>>
[转]IOS多线程
查看>>
详解spl_autoload_register() 与 __autoload
查看>>
Axure RP Extension for Chrome安装
查看>>
day_10
查看>>
ArcGIS API for JavaScript 入门教程[6] 再讲数据——Map类之可操作图层
查看>>
tfs2012 的体验地址
查看>>
打造专业外观-九宫图
查看>>
让discuz论坛单独版块贴子侧边栏(用户信息栏)关闭的修改办法
查看>>
倒计时
查看>>
Robolectric
查看>>
搜索引擎之全文搜索算法功能实现(基于Lucene)
查看>>
XShell远程连接本地虚机
查看>>