关于青蛙走路问题的理解( 扩展欧几里得的应用 )
题意是:一只青蛙在数轴的一点0处,他只可以往左跳a米或者b米,或者往右跳a米或者b米。 问这只青蛙能走到哪些点。
我们分开来看,先看这只青蛙只能往左往右走a米,得到方程:
0 + ax = want1, 这里want1是指只能走a米能到达的点, x是系数可以是任意整数。
再看这只青蛙只能往左往右走b米,得到方程:
0 + by = want2, 这里want2是指只能走b米能到达的点, y是系数可以是任意整数。
因为这只青蛙两种情况都可以走, 所以我们把上面两个公式加起来得到:
ax + by = want
这样是不是就很熟悉了,没错,根据扩展欧几里得,最终的want就是gcd(a,b)的倍数。比如gcd(a,b)=3,那么want={ ...-6,-3,0,3,6,9... };
现在扩展到青蛙跳的距离有n种方案,那么显然只要取这n个数的gcd就好了。
例题:
A frog lives in a one-dimensional world in the point with the coordinate 0. He needs to get to the point with the coordinate x. For some reason he cannot make jumps of arbitrary length, and can jump only by a1, ..., an in any direction. Is he able to reach x?
Input
The first line contains two integers n and x separated by a space (1 ≤ n ≤ 200000, - 109 ≤ x ≤ 109) — the number of variants of jump length and the coordinate of the point to reach.
The second line contains n integers ai separated by spaces (1 ≤ ai ≤ 109) — the lengths of jumps the frog can make.
Output
Output «YES» (without quotes), if the frog can reach the point x, otherwise output «NO» (without quotes).
Examples Input
3 17 3 5 4
Output
YES
代码:
#include <bits/stdc++.h>
using namespace std;
int gcd( int a, int b )
{
if ( b==0 ) {
return a;
}
return gcd(b,a%b);
}
int main()
{
int n,x,ans,u,a,b;
cin >> n >> x;
if ( n==1 ) {
cin >> ans;
}
else {
cin >> a >> b;
ans = gcd(a,b);
for ( int i=2; i<n; i++ ) {
scanf("%d",&u);
ans = gcd(ans,u);
}
}
if ( x%ans==0 ) cout << "YES" << endl;
else cout << "NO" << endl;
return 0;
}
现在我们再来看一道类似的题目加深理解
题意: 从前有n座塔屹立在一条水平线上,标号1到n, 经过了一次灾难,只剩下了两座塔。有两个和尚,分别叫做 Yuwgna 和 Iaka,想让其重现佛光,便决定轮流修建古塔,其中一个人可以选择修建编号为 i的古塔,对于编号 i 应满足:设现有两古塔编号为 j 与 k ,则 i==j+k 或 i==j-k。每一座古塔不能建造两次。
视其为一个游戏,当某一个和尚不能再建造新塔时视为游戏失败,同样的,另一个和尚获得胜利。
分析:为了方便理解我们让a=4,b=6, n = 125 。
首先我们定义仅存的两座塔标号为a和b, 我们分开来看:
1. 让a作为j,b作为k, 我们能修的塔的编号为want,得到公式
4 + 6x = want,式中x是整数, 那么want = { ...-8,-2,4,10,16,22... } ,因为标号1~n, 自动把负数去掉。
2. 让b作为j,a作为k, 我们能修的塔的编号为want,得到公式
4y + 6 = want ,式中y是整数, 那么want = { ...-6,-2,2,6,10,14... } ,因为标号1~n, 自动把负数去掉。
这样两式相加得: 6(x+1) + 4(y+1) = want , 还是 6x + 4y = want = gcd(a,b), 这样我们能修复的点就是want值,也就是gcd(a,b)的倍数。
Input
第一行输入一个数 t (1<= t <= 500) 代表着将要测试的样例数。
对于每一个例子,第一行输入一个 n (2<= n<= 20000) 并且跟着两个数 a 和 b。
Output
对于每一个例子,输出胜者 (``Yuwgna" 或 ``Iaka”)。假设他们每次都会做出最佳决定。
Sample Input
16 2 1 2 3 1 3 67 1 2 100 1 2 8 6 8 9 6 8 10 6 8 11 6 8 12 6 8 13 6 8 14 6 8 15 6 8 16 6 8 1314 6 8 1994 1 13 1994 7 12
Sample Output
Case #1: Iaka Case #2: Yuwgna Case #3: Yuwgna Case #4: Iaka Case #5: Iaka Case #6: Iaka Case #7: Yuwgna Case #8: Yuwgna Case #9: Iaka Case #10: Iaka Case #11: Yuwgna Case #12: Yuwgna Case #13: Iaka Case #14: Yuwgna Case #15: Iaka Case #16: Iaka
代码:
#include <iostream>
using namespace std;
int gcd( int a, int b )
{
if ( b==0 ) {
return a;
}
return gcd(b,a%b);
}
int main()
{
int ji=1,n,listt,a,b;
cin >> listt;
while ( listt-- ) {
cin >> n >> a >> b;
int ans = n/gcd(a,b);
cout << "Case #" << ji++ << ": ";
if ( ans%2==0 ) {
cout << "Iaka" << endl;
}
else {
cout << "Yuwgna" << endl;
}
}
return 0;
}