题解:B3760 [信息与未来 2021] 掷骰子

· · 题解

题意

有一个特殊的骰子,六个面上是 1\sim6 之间的数字(可重复)。从不同角度拍摄了多张照片,每张照片能看到骰子的三个相邻面。需要根据所有照片推断出骰子各面可能的数字,输出所有可能骰子中六个数字之和的最小值和最大值。

思路

枚举所有可能的骰子配置。

对于骰子的任意三个面,它们同时可见当且仅当这三个面相交于同一个顶点。一个骰子有 8 个顶点,每个顶点连接三个面。

对于每个拍摄结果 (x,y,z),需要检查是否存在一个顶点,其三个面的数字与 x,y,z 匹配(顺序可以不同)。由于骰子可以旋转,同一顶点从不同方向看,三个数字的顺序可能不同,所以需要检查该顶点所有可能的排列。

#include <bits/stdc++.h>
using namespace std;

int n;                     // 拍摄次数
int x[25], y[25], z[25];  // 存储每次拍摄到的三个数字
int a, b, c, d, e, f;     // 分别代表骰子六个面的数字(按特定展开顺序)
int mn = 50, mx = 0;      // 最小和与最大和,初始值设为可能范围外的值

// 检查函数:判断当前骰子配置是否与所有拍摄结果一致
bool check() {
    for (int i = 1; i <= n; i++) {
        int u = x[i], v = y[i], w = z[i]; // 当前拍摄的三个数字

        // 检查当前拍摄结果是否对应骰子的某个顶点视图
        // 骰子的八个顶点,每个顶点连接三个面
        // 每个顶点可能有多种观察顺序,这里列举了常见的四种

        // 顶点1: 由面a,c,d组成
        if ((u == a && v == c && w == d) || (u == a && v == d && w == f) ||
            (u == a && v == f && w == b) || (u == a && v == b && w == c))
            continue; // 匹配成功,检查下一个拍摄结果

        // 顶点2: 由面b,e,c组成  
        if ((u == b && v == e && w == c) || (u == b && v == c && w == a) ||
            (u == b && v == a && w == f) || (u == b && v == f && w == e))
            continue;

        // 顶点3: 由面c,e,d组成
        if ((u == c && v == e && w == d) || (u == c && v == d && w == a) ||
            (u == c && v == a && w == b) || (u == c && v == b && w == e))
            continue;

        // 顶点4: 由面d,e,f组成
        if ((u == d && v == e && w == f) || (u == d && v == f && w == a) ||
            (u == d && v == a && w == c) || (u == d && v == c && w == e))
            continue;

        // 顶点5: 由面e,f,d组成
        if ((u == e && v == f && w == d) || (u == e && v == d && w == c) ||
            (u == e && v == c && w == b) || (u == e && v == b && w == f))
            continue;

        // 顶点6: 由面f,e,b组成
        if ((u == f && v == e && w == b) || (u == f && v == b && w == a) ||
            (u == f && v == a && w == d) || (u == f && v == d && w == e))
            continue;

        return false; // 所有顶点都不匹配,当前骰子配置无效
    }
    return true; // 所有拍摄结果都匹配成功
}

int main() {
    // 输入拍摄次数
    cin >> n;

    // 读入每次拍摄的三个数字
    for (int i = 1; i <= n; i++) {
        cin >> x[i] >> y[i] >> z[i];
    }

    // 枚举所有可能的骰子配置
    // 每个面可以是1到6的数字,总共有6^6=46656种可能
    for (a = 1; a <= 6; a++)        // 上面
        for (b = 1; b <= 6; b++)    // 左面
            for (c = 1; c <= 6; c++) // 前面
                for (d = 1; d <= 6; d++) // 后面(c的对面)
                    for (e = 1; e <= 6; e++) // 下面(b的对面)
                        for (f = 1; f <= 6; f++) { // 底面(a的对面)
                            if (check()) { // 如果当前配置符合所有拍摄结果
                                int sum = a + b + c + d + e + f; // 计算六个面的和
                                mn = min(mn, sum); // 更新最小和
                                mx = max(mx, sum); // 更新最大和
                            }
                        }

    // 输出所有可能骰子中数字和的最小值和最大值
    cout << mn << " " << mx;
    return 0;
}