poj 2142
http://hi.baidu.com/5l2_/blog/item/a0381951b5738a1e367abef0.html 这个文章写的很好。。。按照这位大牛写的。。
大概意思 给定 a b k找到满足ax+by=k 的令|x|+|y|最小(等时令a|x|+b|y|最小)不妨a 〉b
先用扩展欧几里得算法求出 一组解 x0,y0,通解可以表示为x=x0+b/d *t y=y0-a/d *t
|x|+|y|=|x0+b/d *t |+|y0-a/d *t| 这个关于t的函数的最小值应该在t零点附近(因为要求t为整数)取到,再进一步考虑知道在 y0*d/a 附近的两整点里取。故直接验证这两点即可。
因为 设a>b之后
|x0+b/d *t| 单调递增,|y0-a/d*t| 先递减再递增。因斜率a/d>b/d,所以总的|x0+b/d *t |+|y0-a/d *t| 先递减再递增,使y0-a/d*t0=0 的t0附近有最小值。
关于ax + by = d 通解可以表示为x=x0+b/d *t y=y0-a/d *t 因为:
任意的k a*x0+b*y0=k 对于任一解 如果 ax+by=k=ax0+by0 得 a(x-x0)=b(y0-y), d=gcd(a,b) gcd(a/d,b/d)=1 又 (a/d)*(x-x0)=(b/d)(y0-y) 那么 b/d|x-x0 设 x-x0=t*b/d 代入的 y0-y=t*a/d 这样就可得出通解形式 x=x0+b/d *t, y=y0-a/d *t 。。。。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
using namespace std;
int x,y,a1,b1;
int ex_gcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;y=0;
return a;
}
int d=ex_gcd(b,a%b,x,y);
int x1=y;
int y1=x-a/b*y;
x=x1;
y=y1;
return d;
}
int cx(int t)
{
return abs(x+a1*t);
}
int cy(int t)
{
return abs(y-b1*t);
}
int main()
{
int a,b,d,k;bool flag;
int x1,x2,y1,y2,r,ax,ay,r1,r2;
while(scanf("%d%d%d",&a,&b,&k)!=EOF)
{
if(a==0&&b==0&&k==0)
break;
flag=1;
if(a<b)
{
flag=0;swap(a,b);
}
d=ex_gcd(a,b,x,y);
x=x*(k/d);y=y*(k/d);
a1=b/d;b1=a/d;r1=y/b1;
if(y-b1*r1<=0)
r1--;
r2=r1+1;
x1=cx(r1);y1=cy(r1);
x2=cx(r2);y2=cy(r2);
if(x1+y1<x2+y2)r=r1;
else
if(x1+y1>x2+y2)r=r2;
else
{
if(a*x1+b*y1<a*x2+b*y2)
r=r1;
else
r=r2;
}
ax=cx(r);ay=cy(r);
if(flag)
printf("%d %d\n",ax,ay);
else
printf("%d %d\n",ay,ax);
}
return 0;
}