CF2114F 8.29考试T1
Codeforces CF2114F
洛谷链接:https://www.luogu.com.cn/problem/CF2114F
题目简述
给你两个正整数 \(x,k\)。进行以下两种变换之一称为一次操作:
- 选择一个满足 \(1 \le a \le k\) 的正整数 \(a\),使 \(x\) 变为 \(x\cdot a\);
- 选择一个满足 \(1 \le a \le k\) 的正整数 \(a\),使 \(x\) 变为 \(\frac{x}{a}\),要求操作完后 \(x\) 值是整数。
你需要找出使 \(x\) 变为给定正整数 \(y\) 的最小操作次数,或判断无解。
解法
因为是通过乘除来把x变为y,容易想到:x和y中因子相同的部分是不需要进行变换的,所以我们可以先将x和y都除以两者的最大公因数;
由于除完之后x和y是互质的,那么可以发现此时要将x变换成y是一定会经过1的;
换句话说,此时将x变成y的过程,是先将x除以一些数变为1,再把1乘上一些数变成y;
很明显这个过程是可逆的,于是问题就变成了1到x的步数加上1到y的步数;
这样我们就成功把乘除变化的问题变成了两个递推求最优解的问题,分别dfs即可;
代码:
#include<bits/stdc++.h>
using namespace std;
int t,x,y,k,dp[1000005];
bool panduanwujie(int x){//判断除以gcd之后的x和y中有没有大于k的因子,如果有那么就判断无解
for(int i=2;i*i<=x&&i<=k;i++){//枚举因子
while(x%i==0){
x/=i;
}
}
if(x>k) return 0;
else return 1;
}
int gcd(int a,int b){
int r=a%b;
if(r==0) return b;
return gcd(b,r);
}
int dfs(int x){
if(x==1) return 0;//如果目标本来就是1,那么就特判需要0步
if(dp[x]!=0) return dp[x]; //记忆化搜索
if(x<=k) return dp[x]=1;//剪枝:如果目标小于k,那么显然一步就能完成
dp[x]=1e9;
for(int i=k;i>=1;i--){
if(x%i==0){
dp[x]=min(dp[x],dfs(x/i)+1);
}
}
return dp[x];
}
int main(){
cin>>t;
for(int i=1;i<=t;i++){
int d,ans=0;
memset(dp,0,sizeof(dp));
cin>>x>>y>>k;
d=gcd(x,y);
x=x/d; y=y/d;
if(!panduanwujie(x)||!panduanwujie(y)){//判断无解
cout<<"-1"<<endl;
continue;
}
ans=dfs(x)+dfs(y);
cout<<ans<<endl;
}
return 0;
}