POJ 2142 : The Balance-解不定方程,一元线性同余

本文探讨了使用不同质量的砝码称出特定重量物品的两种算法解法。解法一采用扩展欧几里得算法求解特解并找到最小解;解法二通过枚举策略寻找最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

有一天平质量为a和b的砝码已知砝码数量不限且天平左右均可以放砝码,现在要求用天平称出c的物品各需要多少


解法一:

转化为ax+by=c的一组整数解(xy)要求|x|+|y|尽量小

利用扩展欧几里得求出特解x0 和 y0然后给出通项公式

x=x0+a/d*t    

y=y0+b/d*t

  对于方程的全部解x=x0+(a/d)t   y=y0+(b/d)*t
   |x|+|y|= | x + b/g*t | + | y - a/g*t |     
    这个关于t的函数的最小值应该在|y - a/g*t|为零附近,即t=y*g/a 
    在 y*g/a 附近的两整数点里取t,再直接验证这两点即可。 

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
//因为是long long,所以提交时ABS()报错CE了!哭!好吧,自己写一个...

LL ABS(LL x){
if(x<0) return -x;
else return x;
}

void exgcd(LL a,LL b,LL &x,LL &y,LL &d)
{
if(!b)
{
d=a;
x=1;
y=0;
}
else
{
exgcd(b,a%b,y,x,d);
y-=x*(a/b);
}
}

int main(){
LL a,b,c,d;
while(~scanf("%lld%lld%lld",&a,&b,&c)&&a&&b&&c){
LL x,y,d,i;
bool ifsub=0;
if(a<b){
ifsub=1;
i=a;
a=b;
b=i;
}
exgcd(a,b,x,y,d);
x=x*(c/d);
y=y*(c/d);
LL t=y/(a/d),min=99999,u,v;
for(i=t-1;i<=t+1;i++){
LL xx=x+b/d*i;
LL yy=y-a/d*i;
if(ABS(xx)+ABS(yy)<min){
min=ABS(xx)+ABS(yy);
u=ABS(xx);
v=ABS(yy);
}
}
if(ifsub) printf("%lld %lld\n",v,u);
else printf("%lld %lld\n",u,v);
}
}



解法二:

引用自http://blog.youkuaiyun.com/famousdt/article/details/7456609

有三种情况:
ax+by=d;(a,b均在左盘)
ax-by=d;(a在左盘,b和d在右盘)
-ax+by=d;(b在左盘,a和d在右盘)
枚举a,求满足条件的解。


#include<iostream>
#include<cmath>
using namespace std;
int main()
{
int i,a,b,d,x,y,xx,yy;
int sum1,sum2;
while(cin>>a>>b>>d)
{
if(a==0 && b==0 && d==0)break;
int min1=9000000,min2=9000000;
int t=0;
xx=0;yy=0;
for(i=0; ; i++){
if((d-a*i)%b==0)//ax+by=d 和 ax-by=d 两种情况,用绝对值表示,所以合写成一个if
{
yy=abs((d-a*i)/b);
sum1=i+yy;
sum2=a*i+b*yy;
if(sum1<min1)
min1=sum1,min2=sum2,x=i,y=yy;
else if(sum1==min1 && sum2<min2)
min1=sum1,min2=sum2,x=i,y=yy;
if(i>min1 || i*a>min2)//如果x的值比x+y的值都大或者a*x比a*x+b*y 的值都大了就跳出
break;
}
int l=i*a+d;
if(l>0 && l%b==0)//-ax+by=d的情况
{
yy=l/b;
sum1=i+yy;
sum2=a*i+b*yy;
if(sum1<min1)
min1=sum1,min2=sum2,x=i,y=yy;
else if(sum1==min1 && sum2<min2)
min1=sum1,min2=sum2,x=i,y=yy;
if(i>min1 || i*a>min2)//如果x的值比x+y的值都大或者a*x比a*x+b*y 的值都大了就跳出
break;

}
}

cout<<x<<" "<<y<<endl;

}

return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值