列表

详情


LCP 27. 黑盒光线反射

秋日市集上有个奇怪的黑盒,黑盒的主视图为 n*m 的矩形。从黑盒的主视图来看,黑盒的上面和下面各均匀分布有 m 个小孔,黑盒的左面和右面各均匀分布有 n 个小孔。黑盒左上角小孔序号为 0,按顺时针编号,总共有 2(m+n) 个小孔。每个小孔均可以打开或者关闭,初始时,所有小孔均处于关闭状态。每个小孔上的盖子均为镜面材质。例如一个 2\3 的黑盒主视图与其小孔分布如图所示:

image.png{:height="200px"}

店长告诉小扣,这里是「几何学的快问快答」,店长可能有两种操作:

其中:

image.png{:height="200px"}

当光线照至边界时:若边界上的小孔为开启状态,则光线会射出;否则,光线会在小孔之间进行反射。特别地:

  1. 若光线射向未打开的拐角(黑盒顶点),则光线会原路反射回去;
  2. 光线自拐角处的小孔照入时,只有一种入射方向(如自序号为 0 的小孔照入方向只能为 -1

image.png{:height="200px"}

请帮助小扣判断并返回店长每次照入的光线从几号小孔射出。

示例 1:

输入: ["BlackBox","open","open","open","close","open"] [[2,3],[6,-1],[4,-1],[0,-1],[6],[0,-1]]

输出:[null,6,4,6,null,4]

解释: BlackBox b = BlackBox(2,3); // 新建一个 2x3 的黑盒 b.open(6,-1) // 打开 6 号小孔,并沿 y=-x 方向照入光线,光线至 0 号小孔反射,从 6 号小孔射出 b.open(4,-1) // 打开 4 号小孔,并沿 y=-x 方向照入光线,光线轨迹为 4-2-8-2-4,从 4 号小孔射出 b.open(0,-1) // 打开 0 号小孔,并沿 y=-x 方向照入光线,由于 6 号小孔为开启状态,光线从 6 号小孔射出 b.close(6) // 关闭 6 号小孔 b.shoot(0,-1) // 从 0 号小孔沿 y=-x 方向照入光线,由于 6 号小孔为关闭状态,4 号小孔为开启状态,光线轨迹为 0-6-4,从 4 号小孔射出

示例 2:

输入: ["BlackBox","open","open","open","open","close","open","close","open"] [[3,3],[1,-1],[5,1],[11,-1],[11,1],[1],[11,1],[5],[11,-1]]

输出:[null,1,1,5,1,null,5,null,11]

解释:

image.png{:height="300px"}

BlackBox b = BlackBox(3,3); // 新建一个 3x3 的黑盒 b.open(1,-1) // 打开 1 号小孔,并沿 y=-x 方向照入光线,光线轨迹为 1-5-7-11-1,从 1 号小孔射出 b.open(5,1) // 打开 5 号小孔,并沿 y=x 方向照入光线,光线轨迹为 5-7-11-1,从 1 号小孔射出 b.open(11,-1) // 打开 11 号小孔,并沿逆 y=-x 方向照入光线,光线轨迹为 11-7-5,从 5 号小孔射出 b.open(11,1) // 从 11 号小孔沿 y=x 方向照入光线,光线轨迹为 11-1,从 1 号小孔射出 b.close(1) // 关闭 1 号小孔 b.open(11,1) // 从 11 号小孔沿 y=x 方向照入光线,光线轨迹为 11-1-5,从 5 号小孔射出 b.close(5) // 关闭 5 号小孔 b.open(11,-1) // 从 11 号小孔沿 y=-x 方向照入光线,光线轨迹为 11-1-5-7-11,从 11 号小孔射出

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class BlackBox { public: BlackBox(int n, int m) { } int open(int index, int direction) { } void close(int index) { } }; /** * Your BlackBox object will be instantiated and called as such: * BlackBox* obj = new BlackBox(n, m); * int param_1 = obj->open(index,direction); * obj->close(index); */

golang 解法, 执行用时: 1092 ms, 内存消耗: 8.3 MB, 提交时间: 2023-10-08 22:57:39

type BlackBox struct {
	Hole []bool
	P    []int
	N    []int
	T      int
	L      int
	M      int
	Num    int
}

func Constructor(n int, m int) BlackBox {
	Box := new(BlackBox)
	Box.M = m
	Box.Num = n
	l := 2*m + 2*n
	Box.L = l
	Box.Hole = make([]bool, Box.L)
	Box.T = 0
	Box.P = make([]int, Box.L)
	Box.N = make([]int, Box.L)

	for i := 0; i < l; i++ {
		Box.P[i] = (l - i) % l
		Box.N[i] = (m * 2 - i + l) % l
	}

	return *Box
}

func (this *BlackBox) Open(index int, direction int) int {
	if !this.Hole[index] {
		this.T ++
		this.Hole[index] = true
	}
	if this.T == 1 {
		return index
	} else {
		for {
			if direction == 1 {
				index = this.P[index]
			} else {
				index = this.N[index]
			}
			direction = -direction
			if this.Hole[index] {
				break;
			}
		}
	}

	return index
}

func (this *BlackBox) Close(index int) {
	this.Hole[index] = false
	this.T--
}

/**
 * Your BlackBox object will be instantiated and called as such:
 * obj := Constructor(n, m);
 * param_1 := obj.Open(index,direction);
 * obj.Close(index);
 */

python3 解法, 执行用时: 840 ms, 内存消耗: 71 MB, 提交时间: 2023-10-08 22:57:05

from sortedcontainers import SortedList

class BlackBox:

    def __init__(self, n: int, m: int):
        corners, T = {0, m, m + n, m + n + m, m + n + m + n}, m + n + m + n

        def getnext(idx, dr):
            if dr > 0: nid = T - idx
            else: nid = (T - (idx + n) - n) % T
            ndr = dr if nid in corners else -dr
            return nid, ndr

        seen = set()
        glen, gid, node_book = [0] * (m + n + m + n), 0, dict()
        for cur in product(range(m + n + m + n), (1, -1)):
            if cur in seen: continue
            while cur not in seen:
                seen.add(cur)
                node_book[cur] = (gid, glen[gid]); glen[gid] += 1
                cur = getnext(*cur)
            gid += 1

        self.node_book, self.opened_book = node_book, [SortedList() for _ in range(gid)]

    def open(self, index: int, dr: int) -> int:
        for dr in (-dr, dr):
            gid, oid = self.node_book[index, dr]
            if (oid, index) not in self.opened_book[gid]:
                self.opened_book[gid].add((oid, index))

        rid = self.opened_book[gid].bisect_right((oid, index)) % len(self.opened_book[gid])
        res = self.opened_book[gid][rid][1]

        return res


    def close(self, index: int) -> None:
        for dr in (1, -1):
            gid, oid = self.node_book[index, dr]
            self.opened_book[gid].remove((oid, index))

# Your BlackBox object will be instantiated and called as such:
# obj = BlackBox(n, m)
# param_1 = obj.open(index,direction)
# obj.close(index)

java 解法, 执行用时: 1110 ms, 内存消耗: 53 MB, 提交时间: 2023-10-08 22:55:36

public class BlackBox {
    boolean[] state;// 存储index下标小洞的打开状态:false表示关闭,true表示打开
    int[] clock;// 存储index小洞沿y=x方向将要照射的下一个小洞的index
    int[] anticlock;// 存储index小洞沿y=-x方向将要照射的下一个小洞的index

    BlackBox(int n, int m) {
        int cnt = 2 * (m + n);
        state = new boolean[cnt];
        clock = new int[cnt];
        anticlock = new int[cnt];
        for (int index = 0; index < cnt; index++) {
            clock[index] = (cnt - index + cnt) % cnt;
            anticlock[index] = (2 * m - index + cnt) % cnt;
            state[index] = false;
        }
    }

    public int open(int index, int direction) {
        int I = index;// 保存起点
        state[index] = true;// 打开小洞
        // 选择下一个反射点
        if (direction == 1)
            index = clock[index];
        else
            index = anticlock[index];
        // 改变反射方向
        direction *= -1;
        // 反射操作尽量不要封装成函数,否则容易超时
        while (state[index] == false) {// 若该点是闭合状态,则继续反射
            // 如果下一个点是角,则光最终会原路返回起点
            if (clock[index] == index || anticlock[index] == index)
                return I;
            // 选择下一个反射点
            if (direction == 1)
                index = clock[index];
            else
                index = anticlock[index];
            // 改变反射方向
            direction *= -1;
        }

        return index;// 若该点是打开状态,则从该点射出
    }

    public void close(int index) {
        state[index] = false;
    }
};


/**
 * Your BlackBox object will be instantiated and called as such:
 * BlackBox obj = new BlackBox(n, m);
 * int param_1 = obj.open(index,direction);
 * obj.close(index);
 */

cpp 解法, 执行用时: 184 ms, 内存消耗: 75.1 MB, 提交时间: 2023-10-08 22:55:04

class BlackBox {
private:
    // 存储从每个小孔以 y=x 方向射出时,所在的循环的 id 以及在循环中的 id
    vector<pair<int, int>> groupPos;
    // 存储从每个小孔以 y=-x 方向射出时,所在的循环的 id 以及在循环中的 id
    vector<pair<int, int>> groupNeg;
    // 存储每个循环的有序映射
    vector<map<int, int>> groupStats;

public:
    BlackBox(int n, int m) {
        int ptCount = (n + m) * 2;
        groupPos.assign(ptCount, {-1, -1});
        groupNeg.assign(ptCount, {-1, -1});
        for (int i = 0; i < ptCount; ++i) {
            // 如果不是左上角或者右下角的小孔,那么从 y=x 方向射出找循环
            if (i != 0 && i != m + n && groupPos[i].first == -1) {
                createGroup(n, m, i, 1);
            }
            // 如果不是左下角或者右上角的小孔,那么从 y=-x 方向射出找循环
            if (i != m && i != m * 2 + n && groupNeg[i].first == -1) {
                createGroup(n, m, i, -1);
            }
        }
    }

    void createGroup(int n, int m, int index, int direction) {
        int groupId = groupStats.size();
        int groupLoc = 0;
        groupStats.emplace_back();
        // 不断模拟光线的路径,直到走到一个已经遇见过的状态,这样就找到了一个循环
        while (!(direction == 1 && groupPos[index].first != -1) && !(direction == -1 && groupNeg[index].first != -1)) {
            if (direction == 1) {
                groupPos[index] = {groupId, groupLoc++};
                index = (n + m) * 2 - index;
            }
            else {
                groupNeg[index] = {groupId, groupLoc++};
                if (index <= m * 2) {
                    index = m * 2 - index;
                }
                else {
                    index = (m * 2 + n) * 2 - index;
                }
            }
            // 如果小孔不在角上,就改变方向
            if (index != 0 && index != m && index != m + n && index != m * 2 + n) {
                direction = -direction;
            }
        }
    }
    
    int open(int index, int direction) {
        // 插入二元组
        if (auto [groupId, groupLoc] = groupPos[index]; groupId != -1) {
            groupStats[groupId].emplace(groupLoc, index);
        }
        if (auto [groupId, groupLoc] = groupNeg[index]; groupId != -1) {
            groupStats[groupId].emplace(groupLoc, index);
        }

        // 查询
        auto [groupId, groupLoc] = (direction == 1 ? groupPos[index] : groupNeg[index]);
        auto& store = groupStats[groupId];
        if (auto iter = store.upper_bound(groupLoc); iter != store.end()) {
            return iter->second;
        }
        else {
            return store.begin()->second;
        }
    }
    
    void close(int index) {
        // 删除二元组
        if (auto [groupId, groupLoc] = groupPos[index]; groupId != -1) {
            groupStats[groupId].erase(groupLoc);
        }
        if (auto [groupId, groupLoc] = groupNeg[index]; groupId != -1) {
            groupStats[groupId].erase(groupLoc);
        }
    }
};

/**
 * Your BlackBox object will be instantiated and called as such:
 * BlackBox* obj = new BlackBox(n, m);
 * int param_1 = obj->open(index,direction);
 * obj->close(index);
 */

上一题