初始有 a, b 两个正整数,每次可以从中选一个大于 1 的数减 1,最后两个都
会减到 1,我们想知道在过程中两个数互质的次数最多是多少。
第一行一个正整数 test ( 1≤test≤1000000 ) 表示数据组数。
接下来 test 行,每行两个正整数a , b (1≤a,b≤1000)。
对于每组数据,一行一个整数表示答案。
1
2 3
4
样例解释
2 3 -> 1 3 -> 1 2 -> 1 1
显然,任意一对( a ,b )都有唯一对应的答案。我们假设如果a=1,那么b可以从b一直减到1,减的过程中一直都是互质的状态,即答案就为b。我就在想是不是可以用递归的思想呢?首先我们知道如果a=1,那么答案就是b。那怎么实现递归呢?**假设递归函数solve(a , b)能够求出输入( a ,b )的解,那么solve(a , b)必是可以由solve(a-1 , b)和solve(a , b-1)求出来。**我们假设以下两种情况:
综上,递归可以这样写:solve(a , b)=max( solve(a-1 , b) , solve(a , b-1) )+(__gcd(a,b)==1)
既然构造出来递归的算法,那么我们是否可以直接AC了呢?其实我们还必须要考虑时间复杂度,递归算法的时间复杂度普遍是比较高的,test的范围是 ( 1≤test≤1000000 ),test次样例的时间代价时间代价是很高的,会直接TLE。
那么我们可以考虑把递归算出来的结果存储起来,怎么存储呢?最好的存储策略就是二维数组,用二维数组实现记忆化搜索,如果把递归算出来的结果存储起来,那么下次遇到同样的问题就不用再重复递归了,直接从数组里面找答案就好啦,这样直接提升了计算效率。
记忆化搜索即可顺利AC。
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=400;
int a,b,T;
int ans=-1;
map<pair<int,int>,int>mp;
int v[1005][1005];
template<class T> void qr(T &x)
{
int f=0;
x=0;
char c=getchar();
for(; !isdigit(c); c=getchar())
f^=c=='-';
for(; isdigit(c); c=getchar())
x=(x<<3)+(x<<1)+(c^48);
x=f?(-x):x;
}
template<class T> void qw(T x)
{
if(x>=10)
qw(x/10);
putchar(x%10+'0');
}
int solve(int a,int b)
{
if(a>b)
swap(a,b);
if(v[a][b])
return v[a][b];
if(a==1)
{
v[a][b]=b;
return v[a][b];
}
else
{
v[a-1][b]=solve(a-1,b);
v[a][b-1]=solve(a,b-1);
v[a][b]= max(v[a-1][b],v[a][b-1])+(__gcd(a,b)==1);
return v[a][b];
}
}
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out1.txt","w",stdout);
#endif
for(int i=1; i<1002; i++)
{
v[1][i]=v[i][1]=i;
}
a=b=1000;
solve(a,b);
qr(T);
//T=1;
while(T--)
{
ans=0;
qr(a);
qr(b);
//scanf("%d%d",&a,&b);
//a=b=1000;
if(a>b)swap(a,b);
if(v[a][b])
ans=v[a][b];
else
ans=solve(a,b);
//printf("%d %d\n",a,b);
//printf("%d\n",ans);
qw(ans);
puts("");
}
}
记忆化搜索基本能够转化成动态规划的题目。
首先我们假设dp[ i ][ j ] 表示 i 和 j 两个数互质的最多次数 。
和Solution1相同,dp [ 1 ][ i ] 和 dp[ i ][ 1 ]都是 i (1<=i<=1000),状态转移方程为 dp[ i ] [j ]=max(dp[ i-1 ][ j ],dp[ i ][ j-1 ])+(__gcd( i, j)==1) 。
有了状态转移方程,就可以轻松写出动态规划的题目了,代码如下。
#include
#include
#include
#include
#include
#include
using namespace std;
const int N=400;
int a,b,T;
int ans=-1;
int dp[1005][1005];
template<class T> void qr(T &x)
{
int f=0;
x=0;
char c=getchar();
for(; !isdigit(c); c=getchar())
f^=c=='-';
for(; isdigit(c); c=getchar())
x=(x<<3)+(x<<1)+(c^48);
x=f?(-x):x;
}
template<class T> void qw(T x)
{
if(x>=10)
qw(x/10);
putchar(x%10+'0');
}
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out1.txt","w",stdout);
#endif
for(int i=1; i<1002; i++)
{
dp[1][i]=dp[i][1]=i;
}
for(int i=2; i<1002; i++)
{
for(int j=2; j<1002; j++)
dp[i][j]=max(dp[i-1][j],dp[i][j-1])+(__gcd(i,j)==1);
}
qr(T);
//T=1;
while(T--)
{
ans=0;
qr(a);
qr(b);
//scanf("%d%d",&a,&b);
//a=b=1000;
if(a>b)swap(a,b);
ans=dp[a][b];
//printf("%d %d\n",a,b);
//printf("%d\n",ans);
qw(ans);
puts("");
}
}
其实做完这道题,自己的收获还是不小的。一道算法题从不同的角度出发,可能会有多种解法。这道题既巩固了我记忆化搜索的相关知识,同时用使得我对动态规划的使用更灵活一点了。(也许以后的动态规划题目,我可以先从记忆化搜索出发去找状态转移方程,嘿嘿,或许对动态规划就能有不一样的体验了)