概述
The 2021 ICPC Asia Nanjing Regional Contest
Problem
给出两个long long范围内的整数a和b(不相等),每次操作可以对a和b进行以下三个操作之一
- a = a + 1, b = b + 1
- a = a - 1, b = b - 1
- a % d = 0 且 b % d = 0 且 d 是质数, a = a / d, b = b / d
问: 最小需要多少次操作才可将 a 或 b 变为 1
Input
5
4 7
9 8
32 84
11 35
2 1
Output
2
7
5
4
0
Solution
- 分析发现,只通过前两个操作,c(c = b - a)是不会改变的,而且a和b的公共质因数一定是c的质因数
(假设a和b的公共质因数为g)。 - 所以,当我们想要进行第三个操作时,我们可以通过前两个操作来到最近的 (a / g, b / g)
- 观察发现,当 b - a = 1时,无论怎么进行操作,gcd(a, b) ≡ equiv ≡ 1,所以最少的操作次数是 a - 1(遇到可直接剪枝)
- 然后就是要注意代码的一些细节问题:
- 看完以上几点后应该感觉到是搜索+剪枝了,但需要进行记忆化加速搜索过程
- 对于如何记忆化,我觉得是本题的重点(具体如何记忆化看下面代码实现)
- 可以将 (a, b)或者 是 (a, b - a) 存为 pair对,作为hash_map的第一维。
- 但由于pair不能作为hash_map的第一维,所以需要自己写一个简单的hash函数:
可将(a, b)哈希为一个数来存。例如:x = a × times ×(一个较大的数) + b,然后将 x 作为hash_map的第一维
Code
int c[1001], m;
int ans = inf;
unordered_map<ll, int>mp;
ll h(int a, int b)//哈希函数
{
return a * 1e9 + b;
}
//本题不能单单将a或b当作map的第一维,
//因为可能会出现a相同但b不相同的情况。
//所以可能返回错误的答案,
//因此需要将(a, b)或者(a, b - a)作为哈希map的第一维
int dfs(int a, int b)
{
//记忆化搜索
if (mp[h(a, b)]) return mp[h(a, b)];
//剪枝
if (b - a == 1) return a - 1;
if (a == 1) return 0;
int ans = a - 1;//最差的情况是直接把a减到1
for (int i = 0; i < m; i++)
if ((b - a) % c[i] == 0)//此处要注意判断条件
{
int res = a % c[i];
ans = min({ ans,
res + 1 + dfs(a / c[i], b / c[i]),
c[i] - res + 1 + dfs(a / c[i] + 1, b / c[i] + 1) });
}
return mp[h(a, b)] = ans;
}
int main()
{
IOS;
int T; cin >> T;
while (T--)
{
mp.clear();//可删可不删。本以为不删会速度快,没想到删掉速度快,且memory小了100倍。。可能是hash的数据多了,查找的慢了吧
int a, b; cin >> a >> b;
if (a > b) swap(a, b);
int d = b - a;
m = 0;
for (int i = 2; i <= sqrt(d); i++)
{
if (d % i == 0)
{
c[m++] = i;
while (d % i == 0) d /= i;
}
}
if (d > 1) c[m++] = d;
cout << dfs(a, b) << endl;
}
return 0;
}
最后
以上就是活泼白昼为你收集整理的J. Xingqiu‘s Joke(2021ICPC-南京)(记忆化搜索 + 数论)的全部内容,希望文章能够帮你解决J. Xingqiu‘s Joke(2021ICPC-南京)(记忆化搜索 + 数论)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复