吴昊品游戏核心算法 Round 16 —— 吴昊教你玩口袋妖怪 第五弹 火箭队的秘密基地

 

  谁时拯救这个世界的英雄?在二次元世界中,两个最有爱的反派角色,莫过于火箭队的这两员了。他们为了维护这个宇宙的和平而存在,尽管每次确实有失利了,但是,却给我们留下了深刻的印象。那么,在三次元世界中,谁是拯救这个世界的英雄呢?我认为,此人乃是阿桑奇。

  好了,这里不说到底是谁在拯救这个世界了,不过,这一弹确实和火箭队有关。

 

  如图所示,在云雾缭绕的氛围中有若干个黑色的地洞,而位于地洞P的那个房间坐着的人,就是板木老人了。我们这里以N点作为起始点,每进入一个地洞,则会有一种对应的转移(当然,这种转移可以是双向的,对应为无向图),我们的目的是到达P点,当然,我们可以尝试很多方式,也愿意避免碰到火箭队的内部队员的挑战。我们如何可以方便地到达P点呢?(这里的方便指明了两点,第一,转移的次数可以尽可能少,第二,尽量可以避免遇到前来挑战的人),明确了这两点之后,改问题转化为了一个带权重(widget的值应该就是挑战者的能力了)的最短路问题了。

 

  这又是一个火箭队的秘密基地,不过,这里的情况貌似比之前的要简单许多,多了几道门,少了几个状态转移区域。我们可以采用同样的方式,来利用AI,用最短的转移次数到达我们所需要到达的目标点。我们将每一个转移区域按照序号分别标注,因为挑战者的干扰,这里对每一条边考虑一个权重(这里的权重是正值,因为,即使是没有火箭队的内部人员存在,我们的权重也是大于等于1的(这一步算你转移耗费的能量,然后,以此作为衡量标准来衡量那些脑残的火箭队挑战者的权重)),考虑到权重的恒正,我们可以利用Dijkstra算法来计算最短路径了!

 

  Dijkstra奥义:

  Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。Dijkstra一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表的方式,这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权边。

 

  步骤如下:

  STEP 1: 初使时令 S={V0},T={其余顶点}T中顶点对应的距离值若存在<V0,Vi>d(V0,Vi)<V0,Vi>弧上的权值若不存在<V0,Vi>d(V0,Vi)

  STEP 2: T中选取一个其距离值为最小的顶点W且不在S中,加入S

  STEP 3: 对其余T中顶点的距离值进行修改:若加进W作中间顶点,从V0Vi的距离值缩短,则修改此距离值重复上述步骤23,直到S中包含所有顶点,即W=Vi为止

 

  Input:每组数据第一行包含两个正整数NM(0<N<200,0<M<1000),分别代表现有转移区域的数目和转移的路径(这里的边是给定的)。接下来是M行转移路径信息。每一行有三个整数A,B,X,表示转移区域A和转移区域B之间的权重(这个概念在之前已经解释过了)。再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。

  Output:计算耗费能量的最小值,如果不能到达的话,输出-1

 

  Solve:

 这里的时间复杂度是ON^2,还可以利用比较好的数据结构将该算法进行进一步的优化,Dijkstra算法最好的复杂度也是O(NlogN+E),用Fibonacci堆来实现优先队列就可以达到该复杂度。

 

  1 //C++和C的输入输出流通用
  2 
  3  #include <iostream>
  4 
  5  #include <cstdio>
  6 
  7  using namespace std;
  8 
  9  
 10 
 11  #define  Max 210
 12 
 13  
 14 
 15  //设置一个极大的常数,INTMAX,由于INT本身是32位的,所以,这个INTMAX已经非常大了
 16 
 17  const int INTMAX=1<<30;
 18 
 19  int p[Max][Max];
 20 
 21  int dist[Max],s[Max];
 22 
 23  int n,m,v;
 24 
 25  
 26 
 27  //在初始化的时候,将所有的边设为最大值
 28 
 29  void init()
 30 
 31  {
 32 
 33    for (int i=0;i<n;i++)
 34 
 35      for (int j=0;j<n;j++)
 36 
 37        p[i][j]=INTMAX;
 38 
 39  }
 40 
 41  
 42 
 43  void dijkstra()
 44 
 45  {
 46 
 47    int i,w,j,min,u;
 48 
 49    for (i=0;i<n;i++)
 50 
 51    {
 52 
 53      //这里说明起始点到每一条边的距离
 54 
 55      dist[i]=p[v][i];
 56 
 57      s[i]=0;
 58 
 59    }
 60 
 61    s[v]=1;
 62 
 63    //自己到自己是没有长度的
 64 
 65    dist[v]=0;
 66 
 67    for (i=0;i<n;i++)
 68 
 69    {
 70 
 71      min=INTMAX;
 72 
 73      u=v;
 74 
 75      //找到到某一点(最开始是起始点)的最短路径
 76 
 77      for (j=0;j<n;j++)
 78 
 79        if (!s[j]&&dist[j]<min)
 80 
 81          min=dist[u=j];
 82 
 83      s[u]=1;
 84 
 85      //找到一条可以代替的最短路
 86 
 87      for (w=0;w<n;w++)
 88 
 89        if (!s[w]&&dist[u]+p[u][w]<dist[w]&&p[u][w]<INTMAX)
 90 
 91          dist[w]=dist[u]+p[u][w];
 92 
 93    }
 94 
 95  }
 96 
 97  
 98 
 99  int main()
100 
101  {
102 
103    int i,j,a,b,c,k;
104 
105    //开始,先输入起点和对应的边
106 
107    while (cin>>n>>m)
108 
109    {
110 
111      //初始化各条边
112 
113      init();
114 
115      for (i=0;i<m;i++)
116 
117      {
118 
119        scanf("%d%d%d",&a,&b,&c);
120 
121        //这里注意到边是双向的,我们既可以转移过去,也可以转移回来
122 
123        if (c<p[a][b])
124 
125          p[a][b]=p[b][a]=c;       
126 
127      }
128 
129      //这里输入起始点和终止点
130 
131      cin>>v>>k;
132 
133      dijkstra();
134 
135      //这里说明路径找不到
136 
137      if (dist[k]==INTMAX) cout<<"-1"<<endl;        
138 
139      else cout<<dist[k]<<endl;   
140 
141    }
142 
143    return 0;
144 
145  }

posted on 2013-04-01 10:49  吴昊系列  阅读(471)  评论(0编辑  收藏  举报

导航