题意:给你一个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; }