diff --git a/src/test.cpp b/src/test.cpp index adddccb..71f974a 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1,247 +1,67 @@ #include #include -#include #include -#include #include +#include -// --- 常量定义 --- -// 使用 enum class 增强类型安全和代码可读性 -enum class TileType : char { - WALL = '#', - FLOOR = '.', - HOUSE_SPACE = 'H', // 临时标记,最终会变回 FLOOR - EXTERIOR = ' ' // 临时标记,用于洪水填充 -}; +// 使用 C++11 的随机数生成工具,比 rand() 质量更高 +std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count()); -// --- 辅助结构体:矩形房间 --- -struct Rect { - int x, y, w, h; +/** + * @brief 生成一个在 [min, max] 范围内的均匀分布的浮点数 + * @param min 范围下界 + * @param max 范围上界 + * @return 随机浮点数 + */ +double random_double(double min, double max) { + std::uniform_real_distribution dist(min, max); + return dist(rng); +} - // 检查此矩形是否与另一个矩形相交(包括边缘接触) - bool intersects(const Rect& other) const { - return (x < other.x + other.w && x + w > other.x && - y < other.y + other.h && y + h > other.y); - } - - // 获取中心点坐标 - std::pair center() const { - return {x + w / 2, y + h / 2}; - } -}; - -// --- 地图生成器类 --- -class MapGenerator { -public: - MapGenerator(int width, int height) - : width_(width), height_(height), - // 使用 std::random_device 获取真随机数种子,初始化梅森旋转算法 - rng_(std::random_device{}()) { - if (width_ <= 0 || height_ <= 0) { - throw std::invalid_argument("Map dimensions must be positive."); - } - } - - // 主生成函数 - void generate(int max_rooms, int min_room_size, int max_room_size) { - // 1. 初始化地图,全部填充为墙壁 - initializeMap(); - - // 2. 随机放置房间 - createRooms(max_rooms, min_room_size, max_room_size); - - // 3. 按顺序连接所有房间 - createCorridors(); - - // 4. 识别并填充“房屋空间” - identifyAndFillHouseSpaces(); - } - - // 打印地图到控制台 - void printMap() const { - for (int y = 0; y < height_; ++y) { - for (int x = 0; x < width_; ++x) { - std::cout << static_cast(map_[y][x]); - } - std::cout << std::endl; - } - } - -private: - int width_, height_; - std::vector> map_; - std::vector rooms_; - std::mt19937 rng_; // 高质量随机数生成器 - - // 步骤1: 初始化地图,全部设置为墙壁 - void initializeMap() { - map_.assign(height_, std::vector(width_, TileType::WALL)); - } - - // 步骤2: 创造不重叠的房间 - void createRooms(int max_rooms, int min_room_size, int max_room_size) { - rooms_.clear(); - std::uniform_int_distribution<> size_dist(min_room_size, max_room_size); - std::uniform_int_distribution<> x_dist(1, width_ - max_room_size - 1); - std::uniform_int_distribution<> y_dist(1, height_ - max_room_size - 1); - - // 尝试放置 max_rooms 个房间 - for (int i = 0; i < max_rooms; ++i) { - Rect new_room; - new_room.w = size_dist(rng_); - new_room.h = size_dist(rng_); - new_room.x = x_dist(rng_); - new_room.y = y_dist(rng_); - - // 确保房间在地图边界内 - if (new_room.x + new_room.w >= width_ - 1 || new_room.y + new_room.h >= height_ - 1) { - continue; - } - - // 检查新房间是否与已存在的房间重叠 - bool overlaps = false; - for (const auto& room : rooms_) { - if (new_room.intersects(room)) { - overlaps = true; - break; - } - } - - if (!overlaps) { - carveRoom(new_room); - rooms_.push_back(new_room); - } - } - } - - // 在地图上“挖掘”一个房间 - void carveRoom(const Rect& room) { - for (int y = room.y; y < room.y + room.h; ++y) { - for (int x = room.x; x < room.x + room.w; ++x) { - map_[y][x] = TileType::FLOOR; - } - } - } - - // 步骤3: 连接房间 - void createCorridors() { - if (rooms_.size() < 2) return; - // 按房间位置排序,以连接邻近的房间 - std::sort(rooms_.begin(), rooms_.end(), [](const Rect& a, const Rect& b){ - return (a.x + a.y) < (b.x + b.y); - }); - - for (size_t i = 0; i < rooms_.size() - 1; ++i) { - auto [x1, y1] = rooms_[i].center(); - auto [x2, y2] = rooms_[i+1].center(); - carvePath(x1, y1, x2, y2); - } - } - - // 挖掘一条 L 形路径连接两个点 - void carvePath(int x1, int y1, int x2, int y2) { - // 随机决定先水平挖还是先垂直挖 - if (std::uniform_int_distribution<>(0, 1)(rng_) == 0) { - carveHorizontalTunnel(x1, x2, y1); - carveVerticalTunnel(y1, y2, x2); - } else { - carveVerticalTunnel(y1, y2, x1); - carveHorizontalTunnel(x1, x2, y2); - } - } - - void carveHorizontalTunnel(int x1, int x2, int y) { - for (int x = std::min(x1, x2); x <= std::max(x1, x2); ++x) { - map_[y][x] = TileType::FLOOR; - } - } - - void carveVerticalTunnel(int y1, int y2, int x) { - for (int y = std::min(y1, y2); y <= std::max(y1, y2); ++y) { - map_[y][x] = TileType::FLOOR; - } - } - - // 步骤4: 关键步骤 - 识别并填充房屋空间 - void identifyAndFillHouseSpaces() { - // 使用洪水填充算法(BFS)标记所有外部空间 - // 从 (0,0) 开始,所有能到达的墙壁都被认为是外部的一部分 - std::queue> q; - q.push({0, 0}); - - // 创建一个访问记录数组,防止重复处理 - std::vector> visited(height_, std::vector(width_, false)); - visited[0][0] = true; - map_[0][0] = TileType::EXTERIOR; // 标记为外部 - - // 定义 4 个移动方向 - int dx[] = {0, 0, 1, -1}; - int dy[] = {1, -1, 0, 0}; - - while (!q.empty()) { - auto [cx, cy] = q.front(); - q.pop(); - - for (int i = 0; i < 4; ++i) { - int nx = cx + dx[i]; - int ny = cy + dy[i]; - - // 检查边界 - if (nx < 0 || ny < 0 || nx >= width_ || ny >= height_) continue; - - // 如果邻居是墙壁且未被访问,则将其标记为外部并加入队列 - if (!visited[ny][nx] && map_[ny][nx] == TileType::WALL) { - visited[ny][nx] = true; - map_[ny][nx] = TileType::EXTERIOR; - q.push({nx, ny}); - } - } - } - - // 遍历整个地图,将在上一步中未被标记为 EXTERIOR 的所有 WALL 包围的区域, - // 即原始的 FLOOR 区域,识别为 HOUSE_SPACE。 - // 但根据您的要求,房屋空间也用空地表示,所以我们只需把 EXTERIOR 变回 WALL, - // 把未被触及的 WALL 留作围墙,而所有 FLOOR 区域自然就是我们想要的。 - // 这一步之后,我们实际上已经区分了 "内部墙" 和 "外部墙"。 - - // 最终清理:将临时标记转换回最终的瓦片类型 - for (int y = 0; y < height_; ++y) { - for (int x = 0; x < width_; ++x) { - if (map_[y][x] == TileType::EXTERIOR) { - map_[y][x] = TileType::WALL; // 将外部标记恢复为墙 - } - // 所有原本是 FLOOR 的地方,现在可以被理解为 房屋空间 或 走廊, - // 并且都用 '.' 表示,完美符合要求。 - } - } - } -}; - -// --- 主函数 --- int main() { - // 设置地图参数 - const int MAP_WIDTH = 80; - const int MAP_HEIGHT = 40; - const int MAX_ROOMS = 15; - const int MIN_ROOM_SIZE = 5; - const int MAX_ROOM_SIZE = 10; + // 设置 IO 加速,在 OI/ACM 竞赛中常用 + std::ios_base::sync_with_stdio(false); + std::cin.tie(NULL); - try { - // 创建生成器实例 - MapGenerator generator(MAP_WIDTH, MAP_HEIGHT); + // --- 问题参数 --- + const double L = 3.0; // 木棍总长度 + const int NUM_TRIALS = 10000000; // 模拟试验次数,次数越多结果越接近理论值 - // 生成地图 - generator.generate(MAX_ROOMS, MIN_ROOM_SIZE, MAX_ROOM_SIZE); + int success_count = 0; // 能够构成三角形的成功次数 - // 打印结果 - std::cout << "--- Generated Map ---" << std::endl; - std::cout << "#: Wall, .: Floor/House Space" << std::endl; - generator.printMap(); + // --- 蒙特卡洛模拟 --- + for (int i = 0; i < NUM_TRIALS; ++i) { + // 1. 在木棍上随机选择两个断点 + double break1 = random_double(0.0, L); + double break2 = random_double(0.0, L); + + // 2. 确定两个断点的位置,方便计算三段长度 + double p1 = std::min(break1, break2); + double p2 = std::max(break1, break2); - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; - return 1; + // 3. 计算三段的长度 + double seg1 = p1; + double seg2 = p2 - p1; + double seg3 = L - p2; + + // 4. 检查是否能构成三角形 + // 充要条件:任意一边的长度都小于总长度的一半 + double half_L = L / 2.0; + if (seg1 < half_L && seg2 < half_L && seg3 < half_L) { + success_count++; + } } + // --- 输出结果 --- + double probability = static_cast(success_count) / NUM_TRIALS; + + std::cout << "--- 蒙特卡洛模拟解决木棍折断问题 ---" << std::endl; + std::cout << "木棍长度 (L): " << L << std::endl; + std::cout << "模拟次数 (N): " << NUM_TRIALS << std::endl; + std::cout << "成功次数: " << success_count << std::endl; + std::cout << std::fixed << std::setprecision(6); + std::cout << "模拟得到的概率 P ≈ " << probability << std::endl; + std::cout << "理论概率 P = 0.25" << std::endl; + return 0; }