zoukankan      html  css  js  c++  java
  • 图论算法——最短路系列

    最短路经典算法整理

    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 <iostream>
     2 #include <string.h>
     3 #include <stdio.h>
     4 #include <algorithm>
     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]<inf&&d[k][j]<inf)
    22             d[i][j]=min(d[i][j],d[i][k]+d[k][j]);//fuluoyide弗洛伊德算法
    23     
    24     int Min=inf,p,q;
    25     for(int i=1;i<=n;i++)//zuihuaiqingkuang最坏情况处理
    26         for(int j=1;j<=n;j++)
    27         {
    28             if(i==j)continue;
    29             else
    30             { 
    31               for(int k=1;k<=n;k++)
    32                     b[i][j]=max(b[i][j],min(d[i][k],d[j][k]));
    33             }
    34             if(Min>b[i][j])Min=b[i][j],p=i,q=j;
    35         }
    36     cout<<p<<" "<<q;
    37     return 0;
    38 }

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

    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<iostream>
     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]=0
    3 For (i = 1; i <= n-1; i++)
    4 For (j = 1; j <= E; j++)         //注意要枚举所有边,不能枚举点。
    5    if (dis[u]+w[j]<dis[v])          //u、v分别是这条边连接的两个点。
    6     {
    7        dis[v] =dis[u] + w[j];
    8        pre[v] = u;
    9   }

    不做过多介绍;

    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发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道N1<=N<=500)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。当然,他将付出额外的费用在奶牛上。
    农夫John很狡猾。像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。
    农夫John知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)。
    【输入格式】butter.in
    第一行: 三个数:奶牛数N,牧场数P2<=P<=800),牧场间道路数C(1<=C<=1450)
    第二行到第N+1: 1N头奶牛所在的牧场号。
    N+2行到第N+C+1行:每行有三个数:相连的牧场AB,两牧场间距(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<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     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<min1) min1=tot;
    57     }
    58     cout<<min1;
    59     return 0;
    60 }

    5  并查集

    普遍代码:

     1 #include<iostream>
     2 #include<cstdio>
     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;           //建立新的集合,其仅有的成员是i
    33           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
    ")
    else printf("No ");   
    }
    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]<min[v]) 
                          min[v]=w[u][v];
    c)算法结束: MST即为最小生成树的权值之和
    
    算法分析&思想讲解:
        Prim算法每次循环都将一个蓝点u变为白点,并且此蓝点u与白点相连的最小边权min[u]还是当前所有蓝点中最小的。这样相当于向生成树中添加了n-1次最小的边,最后得到的一定是最小生成树。
      我们通过对下图最小生成树的求解模拟来理解上面的思想。蓝点和虚线代表未进入最小生成树的点、边;白点和实线代表已进入最小生成树的点、边
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     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));   //初始化为maxint
    19     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++)     //找一个与白点相连的权值最小的蓝点k
    25             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<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     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     else
    17     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<<tot;
    69     return 0;
    70 }
  • 相关阅读:
    Java过滤器与SpringMVC拦截器的差异学习笔记
    java拦截器(Interceptor)学习笔记
    java监听器(Listener)学习笔记
    java过滤器Filter笔记
    java反射学习笔记
    程序员大佬推荐的java学习路线
    jmeter java请求
    java代码实现rabbitMQ请求
    fatjar eclipse4.4 java项目的jar包一起打包 net.sf.fjep.fatjar_0.0.32.jar
    jmeter参数化之CSV Data Set Config
  • 原文地址:https://www.cnblogs.com/lyqlyq/p/6736043.html
Copyright © 2011-2022 走看看