直线上的点(数论初步) By ACReaper

首先我们由欧几里德拓展算法可以求得一组解(d,x,y),而这组解通过与ax + by + c = 0中的c / d,如果c是d的倍数则这个点是直线上的一个解。

分析:

设a,b,c为任意整数,g = gcd(a,b),方程ax + by = g的一组解是(x0,y0),则当c是g的倍数时ax + by = c的一组解是(x0*c/g,y0*c/g),当c不是g的倍数时无整数解。

接着我们由这组解就可以求出其它解,如何求呢?分析如下,设基底解为(x0,y0),另外一个解为(x1,y1),则ax0 + by0 = ax1 + by1 = c,整理后有a(x0 - x1) = b(y1 - y0),这里我们在同时除以gcd(a,b),记a' = a / gcd(a,b),b' = b / gcd(a,b),则a(x0 - x1) = b'(y1 - y0),这时a‘,b’一定是互素的,也就是此时他们的最大公约数为1,因为刚刚约掉了他们的最大公约数,所以x0 - x1一定是b‘的倍数,设x0 - x1 = kb’,有a‘ * kb’ = b‘(y1 - y0),有y1 = y0 + ka’,同理易得x1 = x0 - kb‘。


实现如下:

#include <stdio.h>

void gcd(int a,int b,int &d,int &x,int &y){
	if(b == 0){
		d = a,x = 1, y = 0;
	}else{
		int x1,y1;
		gcd(b,a % b,d,x1,y1);
		x = y1;
		y = x1 - (a / b) * y1;
	}
}
int main(){
	int a,b,c;
	int xx[2];//x的范围 
	int yy[2];//y的范围 
	while(scanf("%d%d%d%d%d%d%d",&a,&b,&c,xx,xx + 1,yy,yy + 1) != EOF){
		int d,x,y;
		gcd(a,b,d,x,y);
		if(c % d == 0){//求得方程的一组解 
			x *= c /d;
			y *= c /d;
		}else{
			printf("无解");
			continue;
		}
		int st = (x - xx[0]) / (b / d);//求出k的取值范围 
		int ed = (x - xx[1]) / (b / d);
		int A[100];
		int count = 1;
		A[0] = 0; 
		for(int k = st; k <= ed; k++){//循环推出所有复合的解 
			int temp = y  + k * (a / d);
			if(temp >= yy[0] && temp <= yy[1]){
				A[count++] = k;
			}
		}
		printf("该范围内一共有%d个解\n",count);
		for(int i = 0 ; i < count; i++){
			printf("%d %d\n",x - A[i] * (b / d),y + A[i] * (a / d));
		}
	}
	return 0;
}



posted @ 2013-04-21 11:15  算法黑魔王  阅读(150)  评论(0编辑  收藏  举报