1 条题解
-
0
简单解题思路
问题描述
给定两个数字
n
和m
,模拟一个猜数字游戏的过程:- 数字对
(j, k)
(1 ≤ j < k ≤ n
)是隐藏的,玩家需要通过m
轮提示猜出这对数字。 - 每轮提示要么给出两数之和
s = j + k
,要么给出两数之积p = j * k
。 - 目标是统计所有满足条件的数字对
(j, k)
,使得玩家恰好在m
轮提示后才能确定这对数字。
核心思路
-
预处理所有可能的数字对:
- 预计算所有
(j, k)
的和与积,并存储每个积对应的所有因数对fac[p]
。
- 预计算所有
-
动态规划搜索:
- 使用
f[j][k]
表示猜出(j, k)
所需的最少轮数。 - 通过
search(j, k, dep)
函数模拟猜数字的过程:- 如果当前轮数
dep
是偶数,检查是否可以通过和s = j + k
排除其他可能的(i, s-i)
。 - 如果当前轮数
dep
是奇数,检查是否可以通过积p = j * k
排除其他可能的因数对fac[p]
。
- 如果当前轮数
- 如果当前轮数可以确定
(j, k)
,则更新f[j][k] = dep
。
- 使用
-
统计结果:
- 遍历所有
(j, k)
,统计f[j][k] == m
的数字对,输出它们的数量和具体值。
- 遍历所有
关键步骤
-
预处理:
- 计算所有
(j, k)
的积p = j * k
,并存储每个p
的所有因数对fac[p]
。
- 计算所有
-
搜索与更新:
- 对于每对
(j, k)
,模拟m
轮提示,检查是否能在m
轮内确定该数字对。 - 更新
f[j][k]
为最小的满足条件的轮数。
- 对于每对
-
输出结果:
- 统计所有
f[j][k] == m
的数字对,输出它们的数量和具体值。
- 统计所有
总结
- 核心算法:动态规划 + 模拟猜数字过程。
- 适用场景:逻辑推理游戏、交互式问题求解。
- 优化点:预处理因式分解结果,避免重复计算。
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <algorithm> #include <cmath> using namespace std; const int N = 205; int n, f[N][N], tot[N * N]; pair<int, int> fac[N * N][15]; // 15 试出来的,否则 MLE int search(int a, int b, int dep) { // 搜索 j,k 是否要 i 次才能猜出来 int s = a + b, p = a * b, i, flag = 1; if (dep % 2 == 0) { for (i = 1; i + i < s; i++) { if (i != a && s - i <= n && f[i][s - i] >= dep) { flag = 0; break; } } if (flag) { f[a][b] = dep; } } else { for (i = 1; i <= tot[p]; i++) { if (fac[p][i].first != a && f[fac[p][i].first][fac[p][i].second] >= dep) { flag = 0; break; } } if (flag) { f[a][b] = dep; } } return f[a][b] == dep; } void preset() { for (int i = 1; i < n; i++) { for (int j = i + 1; j <= n; j++) { tot[i * j]++; fac[i * j][tot[i * j]] = make_pair(i, j); } } } int main() { int i, j, k, m, ans = 0; cin >> n >> m; memset(f, 0x3f, sizeof(f)); preset(); for (i = 0; i <= m; i++) { int flag = 0; for (j = 1; j <= n; j++) { for (k = j + 1; k <= n; k++) { if (f[j][k] > m) { flag |= search(j, k, i); } } } if (!flag) { break; } } for (i = 1; i <= n; i++) { for (j = i + 1; j <= n; j++) { if (f[i][j] == m) { ans++; } } } printf("%d\n", ans); for (i = 1; i <= n; i++) { for (j = i + 1; j <= n; j++) { if (f[i][j] == m) { printf("%d %d\n", i, j); } } } return 0; }
- 数字对
- 1
信息
- ID
- 901
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 7
- 已通过
- 1
- 上传者