UVA-1601 万圣节后的早晨 题解答案代码 算法竞赛入门经典第二版

2023-11-13

GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版

以三个点的当前位置作为状态,广度优先遍历,找到终点即为最短次数。

注意:

一次可以移动多个点,但是每个点只能移动一步。在同一次中,B可以移动到A离开前的位置上,即如果A走了,B可以去A之前的位置。因此,这三个点的移动和判断是有先后顺序的。对每个状态遍历时,情况实际上有 3的全排列(值为6),以及每个点移动的可能四种位置: 3! * 4^3。当然因为墙的存在,因此并没有这么多。

由于最高只有3,因此我的全排列写的不怎么优雅,直接嵌套循环完成了。注意每个点可以动,也可以不动,因此我们要考虑只有一个点动,两个点动的情况。写全排列时。如果第一个点动了大现已经遍历过,这时候不能放入队列(因为在其他点不动的情况下,这个状态已经遍历过了)。但是如果后面的点还继续动,那么这并不是一个完整的状态,因此不应该终止全排列。

按照上面的方法做的话,耗时很久,我扣了点细节,最后终于压线AC。(时间限制12000ms)

1. 根据题目描述,很多节点周围都是墙,因此用邻接表效率更高一些。

2. 一个点的坐标位置为1-16, x和y很容易放到一个数字中存储的。相对于每次计算x1 == x2 && y1 == y2, 一个数字的计算次数更少。其实三个结点的xy位置应该可以整合为一个数字的,这样效率会更高。 

3. 我的答案中用到了struct,一些辅助的判断函数,使用引用而不是直接将整个对象值作为参数,性能会提高一些。

#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>

using namespace std;

int graph[20][20];
vector<int> graphVec[300];
int w, h, n;

struct Point {
  char ch;
  int pos;
};
Point initPoints[3];
Point suppPoints[3];

struct Status {
  int pos[3];
  int step;
};
Status origin, terminal;

bool access[300][300][300];

int steps[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };

bool sameLetter(char small, char big) {
  return small == big - 'A' + 'a';
}
bool statusEqual(Status &s1, Status &s2) {
  for (int i = 0; i < n; ++i) {
    if (s1.pos[i] != s2.pos[i]) return false;
  }
  return true;
}

int xy2Num(int x, int y) {
  return x * 17 + y;
}

void num2XY(int num, int *x, int *y) {
  *x = num / 17;
  *y = num % 17;
}

bool judgeAcc(Status &s) {
  if (n == 1) return access[s.pos[0]][0][0];
  if (n == 2) return access[s.pos[0]][s.pos[1]][0];
  if (n == 3) return access[s.pos[0]][s.pos[1]][s.pos[2]];
}

void setAcc(Status &s) {
  if (n == 1) access[s.pos[0]][0][0] = true;
  if (n == 2) access[s.pos[0]][s.pos[1]][0] = true;
  if (n == 3) access[s.pos[0]][s.pos[1]][s.pos[2]] = true;
}

void printStatus(Status &s) {
  int x, y;
  for (int i = 0; i < n; ++i) {
    num2XY(s.pos[i], &x, &y);
    printf("[%d %d] ", x, y);
  }
  printf(" %d\n", s.step);
}

void printGraphVec() {
  int i, j, x, y;
  for(i = 0; i < 300; ++i) {
    if(graphVec[i].size()) {
      num2XY(i, &x, &y);
      printf("%d %d - ", x, y);
      for(j = 0; j < graphVec[i].size(); ++j) {
        num2XY(graphVec[i][j], &x, &y);
        printf("[%d %d] ", x, y);
      }
      putchar('\n');
    }
  }
  putchar('\n');
}

void init() {
  int i, j, k, initLen = 0, suppLen = 0;
  int x, y;
  memset(access, 0, sizeof(access));
  for(i = 0; i < 300; ++i) {
    graphVec[i].clear();
  }
  for (i = 1; i <= h; ++i) {
    while (getchar() != '\n') ;
    for (j = 1; j <= w; ++j)  {
      graph[i][j] = getchar();
      if (graph[i][j] >= 'a' && graph[i][j] <= 'z')
        initPoints[initLen++] = {char(graph[i][j]), xy2Num(i, j)};
      if (graph[i][j] >= 'A' && graph[i][j] <= 'Z')
        suppPoints[suppLen++] = {char(graph[i][j]), xy2Num(i, j)};
    }
  }

  for(i = 2; i < h; ++i) {
    for (j = 2; j < w; ++j)  {
      if(graph[i][j] == '#') continue;
      for(k = 0; k < 4; ++k) {
        x = i + steps[k][0];
        y = j + steps[k][1];
        if(graph[x][y] == '#') continue;
        graphVec[xy2Num(i, j)].push_back(xy2Num(x, y));
      }
    }
  }

  // printGraphVec();

  for (i = 0; i < n; ++i) {
    origin.pos[i] = initPoints[i].pos;
    for (j = 0; j < n; ++j) {
      if (sameLetter(initPoints[i].ch, suppPoints[j].ch)) {
        terminal.pos[i] = suppPoints[j].pos;
        break;
      }
    }
  }
  origin.step = 0;
  terminal.step = 0;
  setAcc(origin);
  // printStatus(origin);
  // printStatus(terminal);
}

bool judgePos(Status &s) {
  int i, j;
  for (i = 0; i < n; ++i) {
    for (j = 0; j < n; ++j) {
      if (i == j) continue;
      if (s.pos[i] == s.pos[j]) return false;
    }
  }
  return true;
}

int compute() {
  int i, j, k, a1, a2, a3;
  int num1, num2, num3, len1, len2, len3;
  queue<Status> qu;
  Status s0, s1, s2, s3;
  qu.push(origin);
  while (!qu.empty()) {
    s0 = qu.front();
    qu.pop();
    // putchar('\n');
    // printStatus(s0);
    s0.step++;
    for (i = 0; i < n; ++i) {
      num1 = s0.pos[i]; len1 = graphVec[num1].size();
      for (a1 = 0; a1 < len1; ++a1) {
        s1 = s0;
        s1.pos[i] = graphVec[num1][a1];
        if (!judgePos(s1)) continue;
        if (!judgeAcc(s1)) {
          // printStatus(s1);
          setAcc(s1); qu.push(s1);
		    }
        if (statusEqual(s1, terminal)) return s1.step;
        for (j = 0; j < n; ++j) {
          if (i == j) continue;
          num2 = s1.pos[j]; len2 = graphVec[num2].size();
          for (a2 = 0; a2 < len2; ++a2) {
            s2 = s1;
            s2.pos[j] = graphVec[num2][a2];
            if (!judgePos(s2)) continue;
            if (!judgeAcc(s2)) {
              // printStatus(s2);
              setAcc(s2); qu.push(s2);
            }
            if (statusEqual(s2, terminal)) return s2.step;
            for (k = 0; k < n; ++k) {
              if (k == i || k == j) continue;
              num3 = s2.pos[k]; len3 = graphVec[num3].size();
              for (a3 = 0; a3 < len3; ++a3) {
                s3 = s2;
                s3.pos[k] = graphVec[num3][a3];
                if (!judgePos(s3)) continue;
                if (!judgeAcc(s3)) {
                  // printStatus(s3);
                  setAcc(s3); qu.push(s3);
                }
                if (statusEqual(s3, terminal)) return s3.step;
              }
            }
          }
        }
        
      }
    }
  }
}

int main() {
  while (scanf("%d %d %d", &w, &h, &n) == 3 && w != 0) {
    init();
    printf("%d\n", compute());
  }
  return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UVA-1601 万圣节后的早晨 题解答案代码 算法竞赛入门经典第二版 的相关文章

  • SpringBoot jar包的部署方式

    centos版本 CentOS Linux release 7 6 1810 Core JDK1 8 一 SpringBoot jar包的部署方式 nohup 后台进程形式 Linux脚本 启动形式 systemd 优雅系统服务形式 sys

随机推荐

  • Tomcat的日志切分和定时删除

    在我负责的一个小系统中 Linux环境下 由于默认日志都是写入在 cattalina out中 我查看日志catalina out 竟然已经到了 40G了 我想做一下 文件内容检索来追踪问题都无法进行 于是我决定删除以前的无用日志 以每日作
  • Qt 与外部exe进程间通信-基于操作系统的消息传递

    步骤 进程A 通过WindowAPI找到需要传递信息的窗口 然后通过windowAPI发送自定义的消息 其实本质上还是window操作系统定义的消息结构 只不过其中有个字段的值被设置成了自己特有的值 const ULONG PTR CUST
  • Java分割字符串(spilt())

    String 类的 split 方法可以按指定的分割符对目标字符串进行分割 分割后的内容存放在字符串数组中 该方法主要有如下两种重载形式 str split String sign str split String sign int lim
  • 学好了Python可以干什么?

    随着我国对编程的重视程度上升 Python编程的学习趋势逐渐低龄化 在全国掀起Python编程热的同时 还是有许多人对于学习Python抱有怀疑 那么我们就来看看学好了Python究竟可以干什么 根据目前就业市场的反馈 我们可以看到Pyth
  • Cscope的使用

    转载自 http blog csdn net dengxiayehu article details 6330200 首先在目录下建立cscope索引文件 find name c gt cscope file cscope Rbkq 这个命
  • ST芯片使用串口 + DMA接收 + 空闲中断处理,有USART1和LPUART

    普通串口 USART1 首先是DMA初始化 DMA初始化 void MX DMA Init void Init with LL driver DMA controller clock enable LL AHB EnableClock LL
  • 最新淘宝主图下载方法-最新淘宝主图下载工具-马赛克视频助手

    做电商的朋友经常需要去下载淘宝上的别人的主图视频 那么淘宝主图视频怎么下载呢 工具 材料 马赛克视频助手 免费工具 www mask4 com 大家自行下载 操作方法 1 先打开马赛克视频助手 2 随便打开一个淘宝主图地址 如果是手机端的朋
  • ORA-00937: not a single-group group function说明及解决方法

    A SELECT list cannot include both a group function such as AVG COUNT MAX MIN SUM STDDEV or VARIANCE and an individual co
  • uni-app调用手机摄像头进行扫码

  • 放弃node-sass,启用sass

    在下载一个新项目时运行 npm run install 发现报错 npm uninstall 异常 Error Could not find any Visual Studio installation to use 或是 You need
  • 滑动平均滤波

    滑动平均滤波是一种常用的数字信号处理技术 它可以有效地去除信号中的噪声 提高信号的质量 滑动平均滤波的原理是对信号的每一个采样点 取其周围的若干个点的平均值作为滤波后的值 这样 信号中的随机噪声会被平均掉 而信号中的有用信息会被保留 用C语
  • 人民日报:密码,让百姓生活更安全

    密码技术是保障网络与信息安全的核心技术和基础支撑 通过加密保护和安全认证两大核心功能 可以完整实现防假冒 防泄密 防篡改 抗抵赖等安全需求 在网络空间中扮演着 信使 卫士 和 基因 的重要角色 信息化 网络化 数字化高度发达的今天 密码技术
  • 【突发】解决remote: Support for password authentication was removed on August 13, 2021. Please use a perso

    今天 github突然宣布 无法通过用户名加密码进行上传代码和访问 git push remote Support for password authentication was removed on August 13 2021 Plea
  • 策略模式+注解,代替if-else

    策略模式 经常在网上看到一些名为 别再if else走天下了 教你干掉if else 等之类的文章 大部分都会讲到用策略模式去代替if else 策略模式实现的方式也大同小异 主要是定义统一行为 接口或抽象类 并实现不同策略下的处理逻辑 对
  • 判断exe是64位还是32位

    右击exe属性 查看兼容模式 如果有windwos vista之前的版本则为32位的 如下图 如果没有windwos vista之前的版本则为64位的 如下图 转载于 https www cnblogs com tiandsp p 9603
  • Dubbo基础学习

    目录 第一章 概念介绍 1 1 什么是RPC框架 1 2 什么是分布式系统 1 3 Dubbo概述 1 3 Dubbo基本架构 第二章 服务提供者 直连 2 1 目录结构和依赖 2 2 model层 2 3 service层 2 4 res
  • 复用推挽输出与推挽输出区别

    GPIO InitStructure GPIO Mode GPIO Mode AF PP 复用推挽输出 GPIO SetBits GPIOE GPIO Pin 5 如果LED1是上拉的话 这时候它被点亮了 GPIO Mode AF PP g
  • QT 调用执行 linux脚本的三种方法

    在linux系统下 Qt执行shell命令的方式有3种 1 QProcess execute ls 2 system ls 3 QProcess process new QProcess process gt start ls 注1 以上3
  • 2020电赛准备总结(四)

    现在是2020 10 6 距离电赛开始还有三天 最近在考虑要不要让自己提前紧张起来 第一次准备这样的比赛 实在是没有经验 还差一口气没顶上去的感觉 最近开始调整状态了 从开学9 10到现在也快一个月了 感觉自己写博客总是在诉苦 像个怨妇一样
  • UVA-1601 万圣节后的早晨 题解答案代码 算法竞赛入门经典第二版

    GitHub jzplp aoapc UVA Answer 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 以三个点的当前位置作为状态 广度优先遍历 找到终点即为最短次数 注意 一次可以移动多个点 但是每个点只能移动一步 在同一次中 B可