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;
}



你可能感兴趣的:(uva 10090 - Marbles 扩展欧几里得)