洛谷P1076 寻宝 题解 模拟
题目链接:https://www.luogu.com.cn/problem/P1076
解题思路:
这道题目是一道标准的模拟题。一开始我们在第 \(1\) 层的 \(p\) 号房间,然后我们要沿着逆时针方向找 \(x\) 把钥匙,然后进入上一层。
我们可以暴力模拟一下这个算法,看看在最坏情况下的的时间复杂度。
如下所述是我能够想到的最坏情况:
有 \(N = 10000\) 层,每层楼有 \(M = 100\) 个房间,每一层我都要寻找 \(x = 1,000,000\) 把钥匙,每一层只有一把钥匙。
这种情况下,我要转 \(10^6\) 圈才能收集 \(x\) 把钥匙,每一圈我遍历了 \(M=100\) 个房间,再算上层数 \(N=10^4\),总的时间复杂度达到了
\[10^6 \times 10^2 \times 10^4 = 10^{12}
\]
是会超时的。
优化:
我可以开一个 \(sum[]\) 数组,其中 \(sum[i]\) 用于记录第 \(i\) 层的房间总共有多少把钥匙。
然后,我假设我刚进入第 \(i\) 层的时候在第 \(p\) 号房间,嫔妾房间上写的号码是 \(x\),那么:
- 如果 \(x\) 不能被 \(sum[i]\) 整除,则我们只需要从 \(p\) 开始逆时针找到第 \(x \text{ mod } sum[i]\) 把钥匙就可以了(这里 \(\text{mod}\) 表示取余数);
- 否则(\(x\) 能被 \(sum[i]\) 整除),则我们只需要从 \(p\) 的前一个房间按照顺时针顺序找到第一把有钥匙的房间就可以了。
比如,我当前在第 \(i\) 层的第 \(p\) 个房间,上面写着数字 \(8\) —— 也就是说我要找到第 \(8\) 把钥匙对应的房间,同时 \(sum[i]=3\),所以我饶了一圈之后回到第 \(p\) 个房间,我还剩下 \(8-3=5\) 把钥匙;我饶了两圈之后回到第 \(p\) 个房间,我还剩下 \(5-3=2\) 把钥匙。所以这跟我一开始就找 \(8 \text{ mod } 3 = 2\) 把钥匙是一样的效果。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
int has[10010][110], num[10010][110], sum[10010], n, m, p, ans;
const int MOD = 20123;
int main() {
cin >> n >> m;
for (int i = 0; i < n; i ++) {
for (int j = 0; j < m; j ++) {
cin >> has[i][j] >> num[i][j];
sum[i] += has[i][j];
}
}
cin >> p;
for (int i = 0; i < n; i ++) {
int val = num[i][p] % sum[i];
ans = (ans + num[i][p]) % MOD;
if (val) { // 如果不能整除,从p开始逆时针找第 val%sum[i]把钥匙
while (true) {
if (has[i][p]) {
val --;
if (!val) break;
p = (p+1) % m;
}
else {
p = (p+1) % m;
}
}
}
else { // 如果能整除,从p-1开始顺时针找第一把钥匙
while (true) {
p = (p+m-1) % m;
if (has[i][p]) break;
}
}
}
cout << ans << endl;
return 0;
}