【Optimal Path】Lift.cpp 奇怪的电梯

问题2: 奇怪的电梯

( lift.pas )

 

要求:此题用floyd和dijstra算法分别完成。

 

问题描述:

呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第i层楼(1<=i<=N)上有一个数字Ki(0<=Ki<=N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:3 3 1 2 5代表了Ki(K1=3,K2=3,……),从一楼开始。在一楼,按“上”可以到4楼,按“下”是不起作用的,因为没有-2楼。那么,从A楼到B楼至少要按几次按钮呢?

 

输入格式:lift.in

输入文件共有二行,第一行为三个用空格隔开的正整数,表示N,A,B(1≤N≤200, 1≤A,B≤N),第二行为N个用空格隔开的正整数,表示Ki。

 

输出格式:lift.out

输出文件仅一行,即最少按键次数,若无法到达,则输出-1。

 

样例

lift.in

lift.out

5 1 5

3 3 1 2 5

3

 



#include <cstdio>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#define min(a, b) (a < b ? a : b)
#define fi "lift.in"
#define fo "lift.out"

namespace Solve
{
  int N; int A; int B;
  int F[200][200];
  void Init_file();
  void Read_data();
  void Work();
  void solve();
}


void Solve::Init_file()
{
  freopen(fi, "r", stdin);
  freopen(fo, "w", stdout);
}

void Solve::Read_data()
{
  scanf("%d%d%d", &N, &A, &B);
  for(int i = 0; i < N; i++)
  {
    for(int j = 0; j < N; j++)
    
      F[i][j] = 100000;
    F[i][i] = 0;
  }
  for(int i = 0; i < N; i++)
  {
    int x;
    scanf("%d", &x);
    if (i + x < N) F[i][i + x] = 1;
    if (i - x >= 0) F[i][i - x] = 1;
  }
}

void Solve::Work()
{
  for(int k = 0; k < N; k++)
    for(int i = 0; i < N; i++) if(i != k)
      for(int j = 0; j < N; j++) if (k != j && i != j)
      {
        F[i][j] = min(F[i][j], F[i][k] + F[k][j]);
      }
    if (F[A - 1][B - 1] == 100000)
  printf("%d", -1);
  else printf("%d", F[A - 1][B - 1]);
}

void Solve::solve()
{
  Init_file();
  Read_data();
  Work();
}

int main()
{
  Solve::solve();
  return 0;
}

然后第二种方法 Dijkstra 还是先建图 每次选一个离dist最小的点加入路径中 再更新其他点的dist值

最后输出Dist[B] 注意dist[i]的初值是Map[A][i]

代码如下

#include <cstdio>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#define MAX 1000000
#define fi "lift.in"
#define fo "lift.out"

namespace Solve
{
  int N; int A; int B;
  int Min;
  int k;
  int dist[500];
  bool Vst[500];
  int Map[200][200];
  void Init_file();
  void Read_data();
  void Work();
  void solve();
}


void Solve::Init_file()
{
  freopen(fi, "r", stdin);
  freopen(fo, "w", stdout);
}

void Solve::Read_data()
{
  scanf("%d%d%d", &N, &A, &B);
  for(int i = 1; i <= N; i++)
  {
    for(int j = 1; j <= N; j++)
      Map[i][j] = MAX;
    Map[i][i] = 0;
  }
  for(int i = 1; i <= N; i++)
  {
    int x;
    scanf("%d", &x);
    if (x == 0) continue;
    if (i + x <= N) Map[i][i + x] = 1;
    if (i - x >= 1) Map[i][i - x] = 1; 
  }
}

void Solve::Work()
{
  for(int i = 1; i <= N; i++)
  {
    dist[i] = Map[A][i];
  }
  Vst[A] = 1;
  for(int i = 1; i <= N - 1; i++) //共标记 N - 1 个点
  {
    //找一个dist最小未被标记的节点K
    int k;
    Min = MAX; 
    for(int j = 1; j <= N; j++)
    {
      if (!Vst[j] && Min > dist[j])
      {
        Min = dist[j];
        k = j;
      }
    }
    if (k == 0) return;
    Vst[k] = 1;
    for(int j = 1; j <= N; j++)
    {
      if (!Vst[j] && dist[j] > dist[k] + Map[k][j])
        dist[j] = Map[k][j] + dist[k];
    }
  }
  if (dist[B] == MAX) printf("%d", -1);
  else printf("%d", dist[B]);
}

void Solve::solve()
{
  Init_file();
  Read_data();
  Work();
}

int main()
{
  Solve::solve();
  return 0;
}

那么 下面是最常用也是最好记的方法 笔者极力推荐的 SPFA 关于这个算法的背景笔者就不多介绍了

SPFA 在形式上和宽度优先搜索非常类似
不同的是宽度优先搜索中一个点出了队列就不可能重新进入队列
但是SPFA中一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本身被改进,于是再次用来改进其它的点
这样反复迭代下去
………………就出答案了
SPFA很快……

#include <cstdio>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#define min(a, b) (a < b ? a : b)
#define fi "lift.in"
#define fo "lift.out"

namespace Solve
{
  int N; int A; int B;
  int L; int R;
  int Map[500][500];
  bool Vst[500];
  int dist[500];
  int queue[500];
  int Father[500];
  void Init_file();
  void Read_data();
  void Work();
  void solve();
}


void Solve::Init_file()
{
  freopen(fi, "r", stdin);
  freopen(fo, "w", stdout);
}

void Solve::Read_data()
{
  scanf("%d%d%d", &N, &A, &B);
  for(int i = 0; i < N; i++)
  {
    for(int j = 0; j < N; j++) Map[i][j] = 987654321;
    Map[i][i] = 0;
  }
  for(int i = 0; i < N; i++)
  {
    int x;
    scanf("%d", &x);
    if (x == 0) continue;
    if (i + x < N) Map[i][i + x] = 1;
    if (i - x >= 0) Map[i][i - x] = 1;
  }
}

void Solve::Work()
{
//  memset(dist, 0x3f, sizeof(dist));
for(int i = 0; i < N; i++)
{
  dist[i] = 98765432;
}
  L = R = 0;
  dist[A - 1] = 0;
  queue[R] = A - 1;
  Vst[A - 1] = 1;
//  printf("%d ", queue[0]);
  while(L <= R)
  {
    int now = queue[L];
  //  printf("%d ", now);
    Vst[now] = 0;
    for(int j = 0; j < N; j++)
    {
      if(Map[now][j])
      {
        if (dist[j] > dist[now] + Map[now][j])
        {
          dist[j] = dist[now] + Map[now][j];
          Father[j] = now;
          if (!Vst[j])
          {
            queue[++R] = j;
            Vst[j] = 1;
          }
        }
      }
    }
    L++;
  }
  if (dist[B - 1] != 98765432)
  printf("%d", dist[B - 1]);
  else printf("%d", -1);
}

void Solve::solve()
{
  Init_file();
  Read_data();
  Work();
}

int main()
{
  Solve::solve();
  return 0;
}

好了 最短路径告一段落

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值