给定平面上的两个格点
P1=(x1,y1)P2=(x2,y2)求线段P1P2上有几个格点
−109<=x1,x2,y1,y2<=109
枚举所有的合法的
数据范围大,我们需要更快的算法
假设我们从(x1,y1)出发已经找到了离它最近的一个格点P0,那么从P0出发走相同长度肯定也是一个整数格点,就这样一路找下去,最终恰好能找到(x2, y2),中间的每一段都是等长的,你可以假设最终到的点不是(x2,y2),而是延伸出去的某个点,那么显然P0就不是离(x1, y1)最近的一个格点了。
所以我们证明了一个性质
线段上的所有的整数格点恰好都在这条线段的K等分点上
要满足等分点都是整数,所以K必须为X距离和Y距离的公约数,我们需要求最大的K,所以就是两个距离的最大公约数了
- 更相减损术
- 辗转相除法
这两种方法是一样的,下面是上面的优化
int gcd(int a, int b) {
while(a != b) {
if (a > b) {
a -= b;
} else {
b -= a;
}
}
return a;
}
int gcd(int a, int b) {
if(b == 0) return a;
return gcd(b, a % b);
}
为什么这样子做是对的呢?
假设 g 为a b的某个公约数,那么对于任意一个g都有如下两个式子
在更相减损的过程当中,所有的公约数信息都没有丢,也不会增加,跟原来一样,但是数据变小了哦,那么一直变小会发生什么呢?
对于任意一个公约数g都满足最后变成的两个相等的数为g的倍数。所以辗转相减之后的数也是最大公约数 gmx 的倍数,假设最后得到的两个数是
其实上面的辗转相减法,每次就是最大公约数的两个系数k1, k2在辗转相减,最终变成1(他们的最大公约数是1),这个辗转相减的过程可以描述为
一个游戏
有向前向后无线延续的格子, 每个格子都写有整数。其中0号格子是起点, 1号格子是终点。 而骰子上只有a,b,-a,-b四个整数,所以根据a和b的值得不同,有可能无法到达终点。掷出四个整数各多少次可以到达终点?
上述问题归结起来就是
1: b∗x′+(a%b)y′=gcd(a,b)
的整数解 (x′,y′)
将
2: a∗y′+b∗(x′−(a/b)∗y′)=gcd(a,b)
所以,解出了1式,就可以由2式得到原方程的解
特殊的,当b = 0时,
下面的代码解决 a∗x+b∗y=gcd(a,b)
int extgcd(int a, int b, int &x, int &y) {
int d = a;
if(b != 0) {
d = extgcd(b, a % b, x, y);
x -= (a / b) * y;
std::swap(x, y);
} else {
x = 1; y = 0; // g x+ 0 * y = g, y can be any number
}
return d;
}
递归算到最后y其实可以取任意整数。但是取的太大容易导致使得最后算出来的解爆int之类的事情
下面我们来看一下最终算出来的解 x0 y0 的绝对值大小情况
由x y的计算方法x -= (a / b) * y可以知道,x y始终是在max(|a|, |b|)的绝对值范围内的,因此a*x + b*y = gcd(a, b)最后算出的x y的绝对值大小跟a b是同一个级别的。
扩展欧几里德其实就是欧几里德,欧几里德其实就是辗转相减,全是一样的!
通解: