uva 10090 - Marbles 扩展欧几里得

题意:给你一个n,c1, n1, c2, n2 ,要保证x*n1 + y*n2 = n 且 要 x*c1 + y*c2最小


首先来复习下扩展欧几里得,对于二元一次方程 ax + by = n , 扩展欧几里得求出 ax + by = gcd(a,b)的一组x0, y0 , 令d = gcd(a,b) ,如果 d|n ,方程有解 。ax + by = d方程通解为 x = x0 + b/d*t , y = y0 - a/d*t  。 

ax + by = n 方程的通解为  x = x0*n/d +b/d*t , y =  y0*n/d - a/d*t 


对于这道题,首先扩展欧几里得算出 n1*x + n2*y = gcd(n1, n2) = d    得一组解x0, y0 

通解x = x0*n / d + b/d*t   ,   y = y0*n/d - a/d*t  因为x >= 0 && y >= 0 所以- x0*n/n2 <= t <= y0*n/n1

x*c1 + y*c2 = x0*n / d *c1 + y0*n/d * c2 + t/d(c1*n2 - c2*n1) 

前面都是不变的,变的是t/d(c1*n2 - c2*n1),我们知道t的范围这就很简单了。

囧的是我把扩展欧几里得写错了。。。

LL exgcd(LL a, LL b, LL &x, LL &y) {
if(b == 0) {
x = 1; y = 0;
return a;
}
LL ret = exgcd(b, a%b, y, x);
y = y-a/b*x;
return ret;
y = y-a/b*x;这里居然写成了y = y-x*a/b;。。。下次绝对要注意,自己推一下也很快


#include <stdio.h>
#define LL long long
LL exgcd(LL a, LL b, LL &x, LL &y) {
	if(b == 0) {
		x = 1; y = 0;
		return a;
	}
	LL ret = exgcd(b, a%b, y, x);
	y = y-a/b*x;
	return ret;
}
int main () {
	LL c1, c2, n1, n2, n, i;
	while(scanf("%lld", &n) != -1 && n) {
		scanf("%lld%lld%lld%lld", &c1, &n1, &c2, &n2);
		LL x0, y0;
		LL d = exgcd(n1, n2, x0, y0);
		if(n%d == 0) {
			x0 *= n/d;
			y0 *= n/d;
		//	printf("%lld %lld\n", x0, y0);
			LL ans1, ans2;
			n2 /= d;
			n1 /= d;
			if(n2*c1 >= n1*c2) {
				LL t = -x0/n2;
				if(x0%n2 != 0 && x0 < 0)	t++;
				ans1 = x0+n2*t;
				ans2 = y0-n1*t;
			}
			else {
				LL t = y0/n1;
				if(y0%n1 != 0 && y0 < 0)
					t--;
				ans1 = x0+n2*t;
				ans2 = y0-n1*t;
			}
			if(ans1 < 0 || ans2 < 0) {
				puts("failed");
			}
			else
				printf("%lld %lld\n", ans1, ans2);
		}
		else
			puts("failed");
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值