学到的东西:ax+by=c 有的欧几里得题目需要区分a,b的大小,比如当c>0时,如果a>b则x不可能为负值,只有y有可能为负值;如果b>a则y不可能为负值,只有x可能为负值!!!
题目:http://bailian.openjudge.cn/practice/2142/
题解:
在天平两侧放若干砝码 和一堆沙子,使天平平衡
即求ax + by = c 的解
若x ,y 为负,表示与沙子放在同一侧,为正表示放在另一侧
题目同时要求 |x|+|y| 最小,在满足其最小的情况下 使 a|x|+b|y|最小
可以先求 使|x|+|y| 最小,在其中找 a|x|+b|y|最小
如何求 |x|+|y|最小?
由 扩展欧几里得算法 可以求出 一组通解(x0,y0)
令 d=gcd(a,b)
那么 x= x0 + t*b/d , y=y0 + t*a/d
|x|+|y| = | x0+t*b/d | + | y0-t*a/d |
我们假设 a>b,若输入中a<b,交换
| x0+t*b/d | 单调递增
| y0- t*a/d | 先减后增
因为 a>b , 所以减的斜率>增的斜率
所以正个函数先减后增
所以 函数必有零点
所以什么时候函数最小?
y0-t*a/d=0时最小
此时 t=y0*d/a
由于取整存在的误差,使函数最小t在t-1到t+1之间
不放心的话 可以扩大范围 t-5到t+5 等等都是没有问题的
#include <iostream>
#include <cstdio>
using namespace std;
int exgcd(int a,int b,int &x,int &y)
{
if(b==0){
x=1;
y=0;
return a;
}
int ans=exgcd(b,a%b,x,y);
int temp=x;
x=y;
y=temp-a/b*y;
return ans;
}
int ads(int a)
{
if(a<0) return -a;
return a;
}
int main()
{
int a,b,c;
int x,y,x0,y0;
while(cin>>a>>b>>c){
if(!a&&!b&&!c) break;
bool f=false;//用来标记a与b是否交换
if(a<b) swap(a,b),f=true;
//为什么要判断a,b的大小!!因为他俩的大小直接影响到x与y的正负;
//从而影响到单调性的判断!!如果a>b则x值永远不可能为负值,所以关于x的值是单调递增,如果a<b则y的值
//永远不可能为负,关于y的值单调递增(在c为负数的前提下)
int d=exgcd(a,b,x,y);
x0=x*c/d,y0=y*c/d;
int t=d*y0/a;
a=a/d,b=b/d;
int add=1e8,mul=1e8;
int ansx,ansy;
for(int i=t-1;i<=t+1;i++){
x=x0+i*b;
y=y0-i*a;
x=ads(x);
y=ads(y);
if(x+y<add){
add=x+y;
ansx=x;
ansy=y;
mul=a*x+b*y;
}
else if(x+y==add){
if(a*x+b*y<mul){
ansx=x;ansy=y;
mul=a*x+b*y;
}
}
}
if(!f) printf("%d %d\n",ansx,ansy);
else printf("%d %d\n",ansy,ansx);
}
}